diff --git a/armsrc/legicrf.h b/armsrc/legicrf.h index 639687558..1c275a1ed 100644 --- a/armsrc/legicrf.h +++ b/armsrc/legicrf.h @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // 2018 AntiCat // diff --git a/armsrc/legicrfsim.h b/armsrc/legicrfsim.h index c1c8a86e2..da70bff0c 100644 --- a/armsrc/legicrfsim.h +++ b/armsrc/legicrfsim.h @@ -1,4 +1,4 @@ -//----------------------------------------------------------------------------- +//----------------------------------------------------------------------------- // (c) 2009 Henryk Plötz // 2018 AntiCat // diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 6b10ddd0d..5e7375d3d 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -1,2070 +1,2070 @@ -//----------------------------------------------------------------------------- -// Merlok - June 2011, 2012 -// Gerhard de Koning Gans - May 2008 -// Hagen Fritsch - June 2010 -// Midnitesnake - Dec 2013 -// Andy Davies - Apr 2014 -// Iceman - May 2014,2015,2016 -// -// 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. -//----------------------------------------------------------------------------- -// Routines to support ISO 14443 type A. -//----------------------------------------------------------------------------- - -#include "mifarecmd.h" -#include - -#ifndef HARDNESTED_AUTHENTICATION_TIMEOUT -# define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) -#endif -#ifndef HARDNESTED_PRE_AUTHENTICATION_LEADTIME -# define HARDNESTED_PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication -#endif - -// send an incomplete dummy response in order to trigger the card's authentication failure timeout -#ifndef CHK_TIMEOUT -# define CHK_TIMEOUT() { \ - ReaderTransmit(&dummy_answer, 1, NULL); \ - uint32_t timeout = GetCountSspClk() + HARDNESTED_AUTHENTICATION_TIMEOUT; \ - while (GetCountSspClk() < timeout) {}; \ - } -#endif - -static uint8_t dummy_answer = 0; - -//----------------------------------------------------------------------------- -// Select, Authenticate, Read a MIFARE tag. -// read block -//----------------------------------------------------------------------------- -void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) -{ - // params - uint8_t blockNo = arg0; - uint8_t keyType = arg1; - uint64_t ui64Key = 0; - ui64Key = bytes_to_num(datain, 6); - - // variables - byte_t isOK = 0; - byte_t dataoutbuf[16] = {0x00}; - uint8_t uid[10] = {0x00}; - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); - break; - }; - - if(mifare_classic_readblock(pcs, cuid, blockNo, dataoutbuf)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Read block error"); - break; - }; - - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } - - crypto1_destroy(pcs); - - if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16); - LED_B_OFF(); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -} - -void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes){ - - bool turnOffField = (arg0 == 1); - - LED_A_ON(); LED_B_OFF(); LED_C_OFF(); - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); - OnError(0); - return; - }; - - if(!mifare_ultra_auth(keybytes)){ - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed"); - OnError(1); - return; - } - - if (turnOffField) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - } - cmd_send(CMD_ACK,1,0,0,0,0); -} - -// Arg0 = BlockNo, -// Arg1 = UsePwd bool -// datain = PWD bytes, -void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) -{ - uint8_t blockNo = arg0; - byte_t dataout[16] = {0x00}; - bool useKey = (arg1 == 1); //UL_C - bool usePwd = (arg1 == 2); //UL_EV1/NTAG - - LEDsoff(); - LED_A_ON(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); - if(!len) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%02X)",len); - OnError(1); - return; - } - - // UL-C authentication - if ( useKey ) { - uint8_t key[16] = {0x00}; - memcpy(key, datain, sizeof(key) ); - - if ( !mifare_ultra_auth(key) ) { - OnError(1); - return; - } - } - - // UL-EV1 / NTAG authentication - if ( usePwd ) { - uint8_t pwd[4] = {0x00}; - memcpy(pwd, datain, 4); - uint8_t pack[4] = {0,0,0,0}; - if (!mifare_ul_ev1_auth(pwd, pack)) { - OnError(1); - return; - } - } - - if( mifare_ultra_readblock(blockNo, dataout) ) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Read block error"); - OnError(2); - return; - } - - if( mifare_ultra_halt() ) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Halt error"); - OnError(3); - return; - } - - cmd_send(CMD_ACK,1,0,0,dataout,16); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -} - -//----------------------------------------------------------------------------- -// Select, Authenticate, Read a MIFARE tag. -// read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes) -//----------------------------------------------------------------------------- -void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) -{ - // params - uint8_t sectorNo = arg0; - uint8_t keyType = arg1; - uint64_t ui64Key = 0; - ui64Key = bytes_to_num(datain, 6); - - // variables - byte_t isOK = 0; - byte_t dataoutbuf[16 * 16]; - uint8_t uid[10] = {0x00}; - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - isOK = 1; - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - isOK = 0; - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - } - - - if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { - isOK = 0; - if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); - } - - for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { - if(mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf + 16 * blockNo)) { - isOK = 0; - if (MF_DBGLEVEL >= 1) Dbprintf("Read sector %2d block %2d error", sectorNo, blockNo); - break; - } - } - - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - } - - if (MF_DBGLEVEL >= 2) DbpString("READ SECTOR FINISHED"); - - crypto1_destroy(pcs); - - LED_B_ON(); - cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16*NumBlocksPerSector(sectorNo)); - LED_B_OFF(); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} - -// arg0 = blockNo (start) -// arg1 = Pages (number of blocks) -// arg2 = useKey -// datain = KEY bytes -void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) -{ - LEDsoff(); - LED_A_ON(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - // free eventually allocated BigBuf memory - BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(true); - - // params - uint8_t blockNo = arg0; - uint16_t blocks = arg1; - bool useKey = (arg2 == 1); //UL_C - bool usePwd = (arg2 == 2); //UL_EV1/NTAG - uint32_t countblocks = 0; - uint8_t *dataout = BigBuf_malloc(CARD_MEMORY_SIZE); - if (dataout == NULL){ - Dbprintf("out of memory"); - OnError(1); - return; - } - - int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); - if (!len) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%d)",len); - OnError(1); - return; - } - - // UL-C authentication - if ( useKey ) { - uint8_t key[16] = {0x00}; - memcpy(key, datain, sizeof(key) ); - - if ( !mifare_ultra_auth(key) ) { - OnError(1); - return; - } - } - - // UL-EV1 / NTAG authentication - if (usePwd) { - uint8_t pwd[4] = {0x00}; - memcpy(pwd, datain, sizeof(pwd)); - uint8_t pack[4] = {0,0,0,0}; - - if (!mifare_ul_ev1_auth(pwd, pack)){ - OnError(1); - return; - } - } - - for (int i = 0; i < blocks; i++){ - if ((i*4) + 4 >= CARD_MEMORY_SIZE) { - Dbprintf("Data exceeds buffer!!"); - break; - } - - len = mifare_ultra_readblock(blockNo + i, dataout + 4 * i); - - if (len) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Read block %d error",i); - // if no blocks read - error out - if (i == 0) { - OnError(2); - return; - } else { - //stop at last successful read block and return what we got - break; - } - } else { - countblocks++; - } - } - - len = mifare_ultra_halt(); - if (len) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Halt error"); - OnError(3); - return; - } - - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Blocks read %d", countblocks); - - countblocks *= 4; - - cmd_send(CMD_ACK, 1, countblocks, BigBuf_max_traceLen(), 0, 0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - BigBuf_free(); - set_tracing(false); -} - -//----------------------------------------------------------------------------- -// Select, Authenticate, Write a MIFARE tag. -// read block -//----------------------------------------------------------------------------- -void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) -{ - // params - uint8_t blockNo = arg0; - uint8_t keyType = arg1; - uint64_t ui64Key = 0; - byte_t blockdata[16] = {0x00}; - - ui64Key = bytes_to_num(datain, 6); - memcpy(blockdata, datain + 10, 16); - - // variables - byte_t isOK = 0; - uint8_t uid[10] = {0x00}; - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - }; - - if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); - break; - }; - - if(mifare_classic_writeblock(pcs, cuid, blockNo, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - break; - }; - - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - }; - - isOK = 1; - break; - } - - crypto1_destroy(pcs); - - if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - - cmd_send(CMD_ACK,isOK,0,0,0,0); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} - -/* // Command not needed but left for future testing -void MifareUWriteBlockCompat(uint8_t arg0, uint8_t *datain) -{ - uint8_t blockNo = arg0; - byte_t blockdata[16] = {0x00}; - - memcpy(blockdata, datain, 16); - - uint8_t uid[10] = {0x00}; - - LED_A_ON(); LED_B_OFF(); LED_C_OFF(); - - clear_trace(); - set_tracing(true); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - if(!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - OnError(0); - return; - }; - - if(mifare_ultra_writeblock_compat(blockNo, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - OnError(0); - return; }; - - if(mifare_ultra_halt()) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - OnError(0); - return; - }; - - if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - - cmd_send(CMD_ACK,1,0,0,0,0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -} -*/ - -// Arg0 : Block to write to. -// Arg1 : 0 = use no authentication. -// 1 = use 0x1A authentication. -// 2 = use 0x1B authentication. -// datain : 4 first bytes is data to be written. -// : 4/16 next bytes is authentication key. -void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) -{ - uint8_t blockNo = arg0; - bool useKey = (arg1 == 1); //UL_C - bool usePwd = (arg1 == 2); //UL_EV1/NTAG - byte_t blockdata[4] = {0x00}; - - memcpy(blockdata, datain, 4); - - LEDsoff(); - LED_A_ON(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - OnError(0); - return; - }; - - // UL-C authentication - if ( useKey ) { - uint8_t key[16] = {0x00}; - memcpy(key, datain+4, sizeof(key) ); - - if ( !mifare_ultra_auth(key) ) { - OnError(1); - return; - } - } - - // UL-EV1 / NTAG authentication - if (usePwd) { - uint8_t pwd[4] = {0x00}; - memcpy(pwd, datain+4, 4); - uint8_t pack[4] = {0,0,0,0}; - if (!mifare_ul_ev1_auth(pwd, pack)) { - OnError(1); - return; - } - } - - if (mifare_ultra_writeblock(blockNo, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - OnError(0); - return; - }; - - if (mifare_ultra_halt()) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - OnError(0); - return; - }; - - if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); - - cmd_send(CMD_ACK,1,0,0,0,0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} - -void MifareUSetPwd(uint8_t arg0, uint8_t *datain){ - - uint8_t pwd[16] = {0x00}; - byte_t blockdata[4] = {0x00}; - - memcpy(pwd, datain, 16); - - LED_A_ON(); LED_B_OFF(); LED_C_OFF(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - OnError(0); - return; - }; - - blockdata[0] = pwd[7]; - blockdata[1] = pwd[6]; - blockdata[2] = pwd[5]; - blockdata[3] = pwd[4]; - if (mifare_ultra_writeblock( 44, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - OnError(44); - return; - }; - - blockdata[0] = pwd[3]; - blockdata[1] = pwd[2]; - blockdata[2] = pwd[1]; - blockdata[3] = pwd[0]; - if (mifare_ultra_writeblock( 45, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - OnError(45); - return; - }; - - blockdata[0] = pwd[15]; - blockdata[1] = pwd[14]; - blockdata[2] = pwd[13]; - blockdata[3] = pwd[12]; - if (mifare_ultra_writeblock( 46, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - OnError(46); - return; - }; - - blockdata[0] = pwd[11]; - blockdata[1] = pwd[10]; - blockdata[2] = pwd[9]; - blockdata[3] = pwd[8]; - if (mifare_ultra_writeblock( 47, blockdata)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); - OnError(47); - return; - }; - - if (mifare_ultra_halt()) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - OnError(0); - return; - }; - - cmd_send(CMD_ACK,1,0,0,0,0); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} - -// Return 1 if the nonce is invalid else return 0 -int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) { - return ((oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ - (oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \ - (oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0; -} - -void MifareAcquireNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain) { - - uint8_t uid[10] = {0x00}; - uint8_t answer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t par[1] = {0x00}; - uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; - uint32_t cuid = 0; - int16_t isOK = 0; - uint16_t num_nonces = 0; - uint8_t cascade_levels = 0; - uint8_t blockNo = arg0 & 0xff; - uint8_t keyType = (arg0 >> 8) & 0xff; - bool initialize = flags & 0x0001; - bool field_off = flags & 0x0004; - bool have_uid = false; - - LED_A_ON(); - LED_C_OFF(); - - BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(true); - - if (initialize) - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_C_ON(); - - for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE-4; i += 4 ) { - - // Test if the action was cancelled - if (BUTTON_PRESS()) { - isOK = 2; - field_off = true; - break; - } - - if (!have_uid) { // need a full select cycle to get the uid first - iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); - continue; - } - switch (card_info.uidlen) { - case 4 : cascade_levels = 1; break; - case 7 : cascade_levels = 2; break; - case 10: cascade_levels = 3; break; - default: break; - } - have_uid = true; - } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { - if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); - continue; - } - } - - // Transmit MIFARE_CLASSIC_AUTH - uint8_t dcmd[4] = {0x60 + (keyType & 0x01), blockNo, 0x00, 0x00}; - AddCrc14A(dcmd, 2); - ReaderTransmit(dcmd, sizeof(dcmd), NULL); - int len = ReaderReceive(answer, par); - - // wait for the card to become ready again - CHK_TIMEOUT(); - - if (len != 4) { - if (MF_DBGLEVEL >= 2) Dbprintf("AcquireNonces: Auth1 error"); - continue; - } - - num_nonces++; - - // Save the tag nonce (nt) - buf[i] = answer[0]; - buf[i+1] = answer[1]; - buf[i+2] = answer[2]; - buf[i+3] = answer[3]; - } - - LED_C_OFF(); - LED_B_ON(); - cmd_send(CMD_ACK, isOK, cuid, num_nonces-1, buf, sizeof(buf)); - LED_B_OFF(); - - if (MF_DBGLEVEL >= 3) DbpString("AcquireNonces finished"); - - if (field_off) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); - } -} - -//----------------------------------------------------------------------------- -// acquire encrypted nonces in order to perform the attack described in -// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened -// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on -// Computer and Communications Security, 2015 -//----------------------------------------------------------------------------- -void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain) { - - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - uint8_t uid[10] = {0x00}; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t par_enc[1] = {0x00}; - uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; - - uint64_t ui64Key = bytes_to_num(datain, 6); - uint32_t cuid = 0; - int16_t isOK = 0; - uint16_t num_nonces = 0; - uint8_t nt_par_enc = 0; - uint8_t cascade_levels = 0; - uint8_t blockNo = arg0 & 0xff; - uint8_t keyType = (arg0 >> 8) & 0xff; - uint8_t targetBlockNo = arg1 & 0xff; - uint8_t targetKeyType = (arg1 >> 8) & 0xff; - bool initialize = flags & 0x0001; - bool slow = flags & 0x0002; - bool field_off = flags & 0x0004; - bool have_uid = false; - - LED_A_ON(); - LED_C_OFF(); - - BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(false); - - if (initialize) - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LED_C_ON(); - - for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 9; ) { - - // Test if the action was cancelled - if(BUTTON_PRESS()) { - isOK = 2; - field_off = true; - break; - } - - if (!have_uid) { // need a full select cycle to get the uid first - iso14a_card_select_t card_info; - if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); - continue; - } - switch (card_info.uidlen) { - case 4 : cascade_levels = 1; break; - case 7 : cascade_levels = 2; break; - case 10: cascade_levels = 3; break; - default: break; - } - have_uid = true; - } else { // no need for anticollision. We can directly select the card - if (!iso14443a_fast_select_card(uid, cascade_levels)) { - if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); - continue; - } - } - - if (slow) - SpinDelayUs(HARDNESTED_PRE_AUTHENTICATION_LEADTIME); - - uint32_t nt1; - if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { - if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth1 error"); - continue; - } - - // nested authentication - uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par_enc, NULL); - - // wait for the card to become ready again - CHK_TIMEOUT(); - - if (len != 4) { - if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth2 error len=%d", len); - continue; - } - - num_nonces++; - if (num_nonces % 2) { - memcpy(buf+i, receivedAnswer, 4); - nt_par_enc = par_enc[0] & 0xf0; - } else { - nt_par_enc |= par_enc[0] >> 4; - memcpy(buf+i+4, receivedAnswer, 4); - memcpy(buf+i+8, &nt_par_enc, 1); - i += 9; - } - } - - LED_C_OFF(); - crypto1_destroy(pcs); - LED_B_ON(); - cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); - LED_B_OFF(); - - if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished"); - - if (field_off) { - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); - } -} - - -//----------------------------------------------------------------------------- -// MIFARE nested authentication. -// -//----------------------------------------------------------------------------- -void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *datain) -{ - // params - uint8_t blockNo = arg0 & 0xff; - uint8_t keyType = (arg0 >> 8) & 0xff; - uint8_t targetBlockNo = arg1 & 0xff; - uint8_t targetKeyType = (arg1 >> 8) & 0xff; - uint64_t ui64Key = 0; - - ui64Key = bytes_to_num(datain, 6); - - // variables - uint16_t rtr, i, j, len; - uint16_t davg = 0; - static uint16_t dmin, dmax; - uint8_t uid[10] = {0x00}; - uint32_t cuid = 0, nt1, nt2, nttmp, nttest, ks1; - uint8_t par[1] = {0x00}; - uint32_t target_nt[2] = {0x00}, target_ks[2] = {0x00}; - - uint8_t par_array[4] = {0x00}; - uint16_t ncount = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - - uint32_t auth1_time, auth2_time; - static uint16_t delta_time = 0; - - LED_A_ON(); - LED_C_OFF(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - // free eventually allocated BigBuf memory - BigBuf_free(); BigBuf_Clear_ext(false); - - if (calibrate) clear_trace(); - set_tracing(true); - - // statistics on nonce distance - int16_t isOK = 0; - #define NESTED_MAX_TRIES 12 - uint16_t unsuccessfull_tries = 0; - if (calibrate) { // for first call only. Otherwise reuse previous calibration - LED_B_ON(); - WDT_HIT(); - - davg = dmax = 0; - dmin = 2000; - delta_time = 0; - - for (rtr = 0; rtr < 17; rtr++) { - - // Test if the action was cancelled - if(BUTTON_PRESS()) { - isOK = -2; - break; - } - - // prepare next select. No need to power down the card. - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); - rtr--; - continue; - } - - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Can't select card"); - rtr--; - continue; - }; - - auth1_time = 0; - if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); - rtr--; - continue; - }; - auth2_time = (delta_time) ? auth1_time + delta_time : 0; - - if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, &auth2_time)) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error"); - rtr--; - continue; - }; - - nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 - for (i = 101; i < 1200; i++) { - nttmp = prng_successor(nttmp, 1); - if (nttmp == nt2) break; - } - - if (i != 1200) { - if (rtr != 0) { - davg += i; - dmin = MIN(dmin, i); - dmax = MAX(dmax, i); - } - else { - delta_time = auth2_time - auth1_time + 32; // allow some slack for proper timing - } - if (MF_DBGLEVEL >= 3) Dbprintf("Nested: calibrating... ntdist=%d", i); - } else { - unsuccessfull_tries++; - if (unsuccessfull_tries > NESTED_MAX_TRIES) { // card isn't vulnerable to nested attack (random numbers are not predictable) - isOK = -3; - } - } - } - - davg = (davg + (rtr - 1)/2) / (rtr - 1); - - if (MF_DBGLEVEL >= 3) Dbprintf("rtr=%d isOK=%d min=%d max=%d avg=%d, delta_time=%d", rtr, isOK, dmin, dmax, davg, delta_time); - - dmin = davg - 2; - dmax = davg + 2; - - LED_B_OFF(); - } -// ------------------------------------------------------------------------------------------------- - - LED_C_ON(); - - // get crypted nonces for target sector - for(i=0; i < 2 && !isOK; i++) { // look for exactly two different nonces - - target_nt[i] = 0; - while(target_nt[i] == 0) { // continue until we have an unambiguous nonce - - // prepare next select. No need to power down the card. - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); - continue; - } - - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Can't select card"); - continue; - }; - - auth1_time = 0; - if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); - continue; - }; - - // nested authentication - auth2_time = auth1_time + delta_time; - - len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, &auth2_time); - if (len != 4) { - if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error len=%d", len); - continue; - }; - - nt2 = bytes_to_num(receivedAnswer, 4); - if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x", i+1, nt1, nt2, par[0]); - - // Parity validity check - for (j = 0; j < 4; j++) { - par_array[j] = (oddparity8(receivedAnswer[j]) != ((par[0] >> (7-j)) & 0x01)); - } - - ncount = 0; - nttest = prng_successor(nt1, dmin - 1); - for (j = dmin; j < dmax + 1; j++) { - nttest = prng_successor(nttest, 1); - ks1 = nt2 ^ nttest; - - if (valid_nonce(nttest, nt2, ks1, par_array)){ - if (ncount > 0) { // we are only interested in disambiguous nonces, try again - if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (ambigous), ntdist=%d", i+1, j); - target_nt[i] = 0; - break; - } - target_nt[i] = nttest; - target_ks[i] = ks1; - ncount++; - if (i == 1 && target_nt[1] == target_nt[0]) { // we need two different nonces - target_nt[i] = 0; - if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#2: dismissed (= nonce#1), ntdist=%d", j); - break; - } - if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: valid, ntdist=%d", i+1, j); - } - } - if (target_nt[i] == 0 && j == dmax+1 && MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (all invalid)", i+1); - } - } - - LED_C_OFF(); - - crypto1_destroy(pcs); - - uint8_t buf[4 + 4 * 4] = {0}; - memcpy(buf, &cuid, 4); - memcpy(buf+4, &target_nt[0], 4); - memcpy(buf+8, &target_ks[0], 4); - memcpy(buf+12, &target_nt[1], 4); - memcpy(buf+16, &target_ks[1], 4); - - LED_B_ON(); - cmd_send(CMD_ACK, isOK, 0, targetBlockNo + (targetKeyType * 0x100), buf, sizeof(buf)); - LED_B_OFF(); - - if (MF_DBGLEVEL >= 3) DbpString("NESTED FINISHED"); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} - -//----------------------------------------------------------------------------- -// MIFARE check keys. key count up to 85. -// -//----------------------------------------------------------------------------- -typedef struct sector_t { - uint8_t keyA[6]; - uint8_t keyB[6]; -} sector_t; - -typedef struct chk_t { - uint64_t key; - uint32_t cuid; - uint8_t cl; - uint8_t block; - uint8_t keyType; - uint8_t *uid; - struct Crypto1State *pcs; -} chk_t; - -// checks one key. -// fast select, tries 5 times to select -// -// return: -// 2 = failed to select. -// 1 = wrong key -// 0 = correct key -uint8_t chkKey( struct chk_t *c ) { - uint8_t i = 0, res = 2; - while( i < 5 ) { - // this part is from Piwi's faster nonce collecting part in Hardnested. - // assume: fast select - if (!iso14443a_fast_select_card(c->uid, c->cl)) { - ++i; - continue; - } - res = mifare_classic_authex(c->pcs, c->cuid, c->block, c->keyType, c->key, AUTH_FIRST, NULL, NULL); - - CHK_TIMEOUT(); - - // if successfull auth, send HALT - // if ( !res ) - // mifare_classic_halt_ex(c->pcs); - break; - } - return res; -} - -uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) { - - if (!iso14443a_fast_select_card(c->uid, c->cl)) - return 2; - - if ( mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL) ) - return 1; - - uint8_t data[16] = {0x00}; - uint8_t res = mifare_classic_readblock(c->pcs, c->cuid, c->block, data); - - // successful read - if ( !res ) { - // data was something else than zeros. - if ( memcmp(data+10, "\x00\x00\x00\x00\x00\x00", 6) != 0) { - memcpy(keyb, data+10, 6); - res = 0; - } else { - res = 3; - } - mifare_classic_halt_ex(c->pcs); - } - return res; -} - -void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { - for (uint8_t s = 0; s < *sectorcnt; s++) { - - // skip already found A keys - if ( found[(s*2)] ) - continue; - - c->block = FirstBlockOfSector( s ); - if ( chkKey( c ) == 0 ) { - num_to_bytes(c->key, 6, k_sector[s].keyA); - found[(s*2)] = 1; - ++*foundkeys; - - if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan A found (%d)", c->block); - } - } -} - -void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { - for (uint8_t s = 0; s < *sectorcnt; s++) { - - // skip already found B keys - if ( found[(s*2)+1] ) - continue; - - c->block = FirstBlockOfSector( s ); - if ( chkKey( c ) == 0 ) { - num_to_bytes(c->key, 6, k_sector[s].keyB); - found[(s*2)+1] = 1; - ++*foundkeys; - - if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan B found (%d)", c->block); - } - } -} - -// loop all A keys, -// when A is found but not B, try to read B. -void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { - - // read Block B, if A is found. - for (uint8_t s = 0; s < *sectorcnt; ++s) { - - if ( found[(s*2)] && found[(s*2)+1] ) - continue; - - c->block = (FirstBlockOfSector( s ) + NumBlocksPerSector( s ) - 1); - - // A but not B - if ( found[(s*2)] && !found[(s*2)+1] ){ - c->key = bytes_to_num(k_sector[s].keyA, 6); - uint8_t status = chkKey_readb(c, k_sector[s].keyB); - if ( status == 0 ){ - found[(s*2)+1] = 1; - ++*foundkeys; - - if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Reading B found (%d)", c->block); - - // try quick find all B? - // assume: keys comes in groups. Find one B, test against all B. - c->key = bytes_to_num( k_sector[s].keyB, 6); - c->keyType = 1; - chkKey_scanB(c, k_sector, found, sectorcnt, foundkeys); - } - } - } -} - - - -// get Chunks of keys, to test authentication against card. -// arg0 = antal sectorer -// arg0 = first time -// arg1 = clear trace -// arg2 = antal nycklar i keychunk -// datain = keys as array -void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { - - // first call or - uint8_t sectorcnt = arg0 & 0xFF; // 16; - uint8_t firstchunk = (arg0 >> 8) & 0xF; - uint8_t lastchunk = (arg0 >> 12) & 0xF; - uint8_t strategy = arg1 & 0xFF; - uint8_t use_flashmem = (arg1 >> 8) & 0xFF; - uint16_t keyCount = arg2 & 0xFF; - uint8_t status = 0; - - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - struct chk_t chk_data; - - uint8_t allkeys = sectorcnt << 1; - - static uint32_t cuid = 0; - static uint8_t cascade_levels = 0; - static uint8_t foundkeys = 0; - static sector_t k_sector[80]; - static uint8_t found[80]; - static uint8_t *uid; - -#ifdef WITH_FLASH - if ( use_flashmem ) { - BigBuf_free(); - uint16_t isok = 0; - uint8_t size[2] = {0x00, 0x00}; - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET, size, 2); - if ( isok != 2 ) - goto OUT; - - keyCount = size[1] << 8 | size[0]; - - if ( keyCount == 0 && keyCount == 0xFFFF) - goto OUT; - - datain = BigBuf_malloc( keyCount * 6); - if (datain == NULL ) - goto OUT; - - isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET+2, datain, keyCount * 6); - if ( isok != keyCount * 6 ) - goto OUT; - - } -#endif - - if (uid == NULL || firstchunk) { - uid = BigBuf_malloc(10); - if (uid == NULL ) - goto OUT; - } - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - LEDsoff(); - LED_A_ON(); - - if ( firstchunk ) { - clear_trace(); - set_tracing(false); - - memset(k_sector, 0x00, 480+10); - memset(found, 0x00, sizeof(found)); - foundkeys = 0; - - iso14a_card_select_t card_info; - if ( !iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys_fast: Can't select card (ALL)"); - goto OUT; - } - - switch (card_info.uidlen) { - case 4 : cascade_levels = 1; break; - case 7 : cascade_levels = 2; break; - case 10: cascade_levels = 3; break; - default: break; - } - - CHK_TIMEOUT(); - } - - // set check struct. - chk_data.uid = uid; - chk_data.cuid = cuid; - chk_data.cl = cascade_levels; - chk_data.pcs = pcs; - chk_data.block = 0; - - // keychunk loop - depth first one sector. - if ( strategy == 1 || use_flashmem) { - - uint8_t newfound = foundkeys; - - uint16_t lastpos = 0; - uint16_t s_point = 0; - // Sector main loop - // keep track of how many sectors on card. - for (uint8_t s = 0; s < sectorcnt; ++s) { - - if ( found[(s*2)] && found[(s*2)+1] ) - continue; - - for (uint16_t i = s_point; i < keyCount; ++i) { - - //if ( i % 100 == 0) Dbprintf("ChkKeys_fast: sector %d | checking %d | %d found | s_point %d", s, i, foundkeys, s_point); - - // Allow button press / usb cmd to interrupt device - if (BUTTON_PRESS() && !usb_poll_validate_length()) { - goto OUT; - } - - // found all keys? - if ( foundkeys == allkeys ) - goto OUT; - - WDT_HIT(); - - // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector - chk_data.block = FirstBlockOfSector( s ); - - // new key - chk_data.key = bytes_to_num(datain + i * 6, 6); - - // skip already found A keys - if( !found[(s*2)] ) { - chk_data.keyType = 0; - status = chkKey( &chk_data); - if ( status == 0 ) { - memcpy(k_sector[s].keyA, datain + i * 6, 6); - found[(s*2)] = 1; - ++foundkeys; - - chkKey_scanA(&chk_data, k_sector, found, §orcnt, &foundkeys); - - // read Block B, if A is found. - chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys); - - chk_data.keyType = 1; - chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); - - chk_data.keyType = 0; - chk_data.block = FirstBlockOfSector( s ); - - if ( use_flashmem ) { - if ( lastpos != i && lastpos != 0) { - if ( i - lastpos < 0xF) { - s_point = i & 0xFFF0; - } - } else { - lastpos = i; - } - } - } - } - - // skip already found B keys - if( !found[(s*2)+1] ) { - chk_data.keyType = 1; - status = chkKey( &chk_data); - if ( status == 0 ) { - memcpy(k_sector[s].keyB, datain + i * 6, 6); - found[(s*2)+1] = 1; - ++foundkeys; - - chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); - - if ( use_flashmem ) { - if ( lastpos != i && lastpos != 0) { - - if ( i - lastpos < 0xF) - s_point = i & 0xFFF0; - } else { - lastpos = i; - } - } - } - } - - if ( found[(s*2)] && found[(s*2)+1] ) - break; - - } // end keys test loop - depth first - - // assume1. if no keys found in first sector, get next keychunk from client - if ( !use_flashmem && (newfound-foundkeys == 0) ) - goto OUT; - - } // end loop - sector - } // end strategy 1 - - if ( foundkeys == allkeys ) - goto OUT; - - if ( strategy == 2 || use_flashmem ) { - - // Keychunk loop - for (uint16_t i = 0; i < keyCount; i++) { - - // Allow button press / usb cmd to interrupt device - if (BUTTON_PRESS() && !usb_poll_validate_length()) break; - - // found all keys? - if ( foundkeys == allkeys ) - goto OUT; - - WDT_HIT(); - - // new key - chk_data.key = bytes_to_num(datain + i * 6, 6); - - // Sector main loop - // keep track of how many sectors on card. - for (uint8_t s = 0; s < sectorcnt; ++s) { - - if ( found[(s*2)] && found[(s*2)+1] ) continue; - - // found all keys? - if ( foundkeys == allkeys ) - goto OUT; - - // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector - chk_data.block = FirstBlockOfSector( s ); - - // skip already found A keys - if( !found[(s*2)] ) { - chk_data.keyType = 0; - status = chkKey( &chk_data); - if ( status == 0 ) { - memcpy(k_sector[s].keyA, datain + i * 6, 6); - found[(s*2)] = 1; - ++foundkeys; - - chkKey_scanA( &chk_data, k_sector, found, §orcnt, &foundkeys); - - // read Block B, if A is found. - chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys); - - chk_data.block = FirstBlockOfSector( s ); - } - } - - // skip already found B keys - if( !found[(s*2)+1] ) { - chk_data.keyType = 1; - status = chkKey( &chk_data); - if ( status == 0 ) { - memcpy(k_sector[s].keyB, datain + i * 6, 6); - found[(s*2)+1] = 1; - ++foundkeys; - - chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); - } - } - } // end loop sectors - } // end loop keys - } // end loop strategy 2 -OUT: - LEDsoff(); - - crypto1_destroy(pcs); - - // All keys found, send to client, or last keychunk from client - if (foundkeys == allkeys || lastchunk ) { - - uint64_t foo = 0; - for (uint8_t m = 0; m < 64; m++) { - foo |= ((uint64_t)(found[m] & 1) << m); - } - - uint16_t bar = 0; - uint8_t j = 0; - for (uint8_t m=64; m < sizeof(found); m++) { - bar |= ((uint16_t)(found[m] & 1) << j++); - } - - uint8_t *tmp = BigBuf_malloc(480+10); - memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t) ); - num_to_bytes(foo, 8, tmp+480); - tmp[488] = bar & 0xFF; - tmp[489] = bar >> 8 & 0xFF; - - cmd_send(CMD_ACK, foundkeys, 0, 0, tmp, 480+10); - - set_tracing(false); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - BigBuf_free(); BigBuf_Clear_ext(false); - } else { - // partial/none keys found - cmd_send(CMD_ACK, foundkeys, 0, 0, 0, 0); - } -} - -void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - uint8_t uid[10] = {0x00}; - - uint64_t key = 0; - uint32_t cuid = 0; - int i, res; - uint8_t blockNo = arg0 & 0xFF; - uint8_t keyType = (arg0 >> 8) & 0xFF; - uint8_t keyCount = arg2; - uint8_t cascade_levels = 0; - uint8_t isOK = 0; - bool have_uid = false; - bool clearTrace = arg1 & 0xFF; - - LEDsoff(); - LED_A_ON(); - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - if (clearTrace) - clear_trace(); - - set_tracing(true); - - for (i = 0; i < keyCount; i++) { - - // Iceman: use piwi's faster nonce collecting part in hardnested. - if (!have_uid) { // need a full select cycle to get the uid first - iso14a_card_select_t card_info; - if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (ALL)"); - --i; // try same key once again - continue; - } - switch (card_info.uidlen) { - case 4 : cascade_levels = 1; break; - case 7 : cascade_levels = 2; break; - case 10: cascade_levels = 3; break; - default: break; - } - have_uid = true; - } else { // no need for anticollision. We can directly select the card - if (!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); - --i; // try same key once again - continue; - } - } - - key = bytes_to_num(datain + i * 6, 6); - res = mifare_classic_auth(pcs, cuid, blockNo, keyType, key, AUTH_FIRST); - - CHK_TIMEOUT(); - - if (res) - continue; - - isOK = 1; - break; - } - - LED_B_ON(); - cmd_send(CMD_ACK, isOK, 0, 0, datain + i * 6, 6); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - - set_tracing(false); - crypto1_destroy(pcs); -} - -//----------------------------------------------------------------------------- -// MIFARE commands set debug level -// -//----------------------------------------------------------------------------- -void MifareSetDbgLvl(uint16_t arg0){ - MF_DBGLEVEL = arg0; - Dbprintf("Debug level: %d", MF_DBGLEVEL); -} - -//----------------------------------------------------------------------------- -// Work with emulator memory -// -// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not -// involved in dealing with emulator memory. But if it is called later, it might -// destroy the Emulator Memory. -//----------------------------------------------------------------------------- - -void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - emlClearMem(); -} - -void MifareEMemSet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - if (arg2==0) arg2 = 16; // backwards compat... default bytewidth - emlSetMem_xt(datain, arg0, arg1, arg2); // data, block num, blocks count, block byte width -} - -void MifareEMemGet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ - FpgaDownloadAndGo(FPGA_BITSTREAM_HF); - byte_t buf[USB_CMD_DATA_SIZE] = {0x00}; - emlGetMem(buf, arg0, arg1); // data, block num, blocks count (max 4) - - LED_B_ON(); - cmd_send(CMD_ACK,arg0,arg1,0,buf,USB_CMD_DATA_SIZE); - LED_B_OFF(); -} - -//----------------------------------------------------------------------------- -// Load a card into the emulator memory -// -//----------------------------------------------------------------------------- -void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ - uint8_t numSectors = arg0; - uint8_t keyType = arg1; - uint64_t ui64Key = 0; - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs; - pcs = &mpcs; - - // variables - byte_t dataoutbuf[16] = {0x00}; - byte_t dataoutbuf2[16] = {0x00}; - uint8_t uid[10] = {0x00}; - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - bool isOK = true; - - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - isOK = false; - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - } - - for (uint8_t sectorNo = 0; isOK && sectorNo < numSectors; sectorNo++) { - ui64Key = emlGetKey(sectorNo, keyType); - if (sectorNo == 0){ - if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { - isOK = false; - if (MF_DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth error", sectorNo); - break; - } - } else { - if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_NESTED)) { - isOK = false; - if (MF_DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth nested error", sectorNo); - break; - } - } - - for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { - if(isOK && mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) { - isOK = false; - if (MF_DBGLEVEL >= 1) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo); - break; - } - if (isOK) { - if (blockNo < NumBlocksPerSector(sectorNo) - 1) { - emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1); - } else { // sector trailer, keep the keys, set only the AC - emlGetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); - memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); - emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); - } - } - } - - } - - if(mifare_classic_halt(pcs, cuid)) - if (MF_DBGLEVEL >= 1) - Dbprintf("Halt error"); - - // ----------------------------- crypto1 destroy - crypto1_destroy(pcs); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - - if (MF_DBGLEVEL >= 2) DbpString("EMUL FILL SECTORS FINISHED"); - - set_tracing(false); -} - - -//----------------------------------------------------------------------------- -// Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) -// -// PARAMS - workFlags -// bit 0 - need get UID -// bit 1 - need wupC -// bit 2 - need HALT after sequence -// bit 3 - need turn on FPGA before sequence -// bit 4 - need turn off FPGA -// bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) -// bit 6 - wipe tag. -//----------------------------------------------------------------------------- -// magic uid card generation 1 commands -uint8_t wupC1[] = { MIFARE_MAGICWUPC1 }; -uint8_t wupC2[] = { MIFARE_MAGICWUPC2 }; -uint8_t wipeC[] = { MIFARE_MAGICWIPEC }; - -void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain){ - - // params - uint8_t workFlags = arg0; - uint8_t blockNo = arg1; - - // detect 1a/1b - bool is1b = false; - - // variables - bool isOK = false; //assume we will get an error - uint8_t errormsg = 0x00; - uint8_t uid[10] = {0x00}; - uint8_t data[18] = {0x00}; - uint32_t cuid = 0; - - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - if (workFlags & MAGIC_INIT) { - LED_A_ON(); - LED_B_OFF(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); - set_tracing(true); - } - - //loop doesn't loop just breaks out if error - while (true) { - // read UID and return to client with write - if (workFlags & MAGIC_UID) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); - errormsg = MAGIC_UID; - } - mifare_classic_halt_ex(NULL); - break; - } - - // wipe tag, fill it with zeros - if (workFlags & MAGIC_WIPE){ - ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); - if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); - errormsg = MAGIC_WIPE; - break; - } - - ReaderTransmit(wipeC, sizeof(wipeC), NULL); - if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wipeC error"); - errormsg = MAGIC_WIPE; - break; - } - - mifare_classic_halt_ex(NULL); - } - - // write block - if (workFlags & MAGIC_WUPC) { - ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); - if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); - errormsg = MAGIC_WUPC; - break; - } - - if ( !is1b ) { - ReaderTransmit(wupC2, sizeof(wupC2), NULL); - if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]"); - is1b = true; - continue; - } - } - } - - if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("write block send command error"); - errormsg = 4; - break; - } - - memcpy(data, datain, 16); - AddCrc14A(data, 16); - - ReaderTransmit(data, sizeof(data), NULL); - if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("write block send data error"); - errormsg = 0; - break; - } - - if (workFlags & MAGIC_HALT) - mifare_classic_halt_ex(NULL); - - isOK = true; - break; - - } // end while - - if (isOK ) - cmd_send(CMD_ACK,1,0,0,uid,sizeof(uid)); - else - OnErrorMagic(errormsg); - - if (workFlags & MAGIC_OFF) - OnSuccessMagic(); -} - -void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain){ - - uint8_t workFlags = arg0; - uint8_t blockNo = arg1; - uint8_t errormsg = 0x00; - bool isOK = false; //assume we will get an error - - // detect 1a/1b - bool is1b = false; - - // variables - uint8_t data[MAX_MIFARE_FRAME_SIZE]; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - memset(data, 0x00, sizeof(data)); - - if (workFlags & MAGIC_INIT) { - LED_A_ON(); - LED_B_OFF(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); - set_tracing(true); - } - - //loop doesn't loop just breaks out if error or done - while (true) { - if (workFlags & MAGIC_WUPC) { - ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); - if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); - errormsg = MAGIC_WUPC; - break; - } - - if ( !is1b ) { - ReaderTransmit(wupC2, sizeof(wupC2), NULL); - if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]"); - is1b = true; - continue; - } - } - } - - // read block - if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 18)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("read block send command error"); - errormsg = 0; - break; - } - - memcpy(data, receivedAnswer, sizeof(data)); - - // send HALT - if (workFlags & MAGIC_HALT) - mifare_classic_halt_ex(NULL); - - isOK = true; - break; - } - // if MAGIC_DATAIN, the data stays on device side. - if (workFlags & MAGIC_DATAIN) { - if (isOK) - memcpy(datain, data, sizeof(data)); - } else { - if (isOK) - cmd_send(CMD_ACK,1,0,0,data,sizeof(data)); - else - OnErrorMagic(errormsg); - } - - if (workFlags & MAGIC_OFF) - OnSuccessMagic(); -} - -void MifareCIdent(){ - #define GEN_1A 1 - #define GEN_1B 2 - #define GEN_2 4 - // variables - uint8_t isGen = 0; - uint8_t rec[1] = {0x00}; - uint8_t recpar[1] = {0x00}; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - // Generation 1 test - ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); - if(!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { - goto TEST2; - }; - isGen = GEN_1B; - - ReaderTransmit(wupC2, sizeof(wupC2), NULL); - if(!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { - goto OUT; - }; - isGen = GEN_1A; - goto OUT; - -TEST2:; -/* - // Generation 2 test - - // halt previous. - mifare_classic_halt(NULL, 0); - - //select - if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { - goto OUT; - }; - - // MIFARE_CLASSIC_WRITEBLOCK 0xA0 - // ACK 0x0a - uint16_t len = mifare_sendcmd_short(null, 1, 0xA0, 0, rec, recpar, NULL); - if ((len != 1) || (rec[0] != 0x0A)) { - isGen = GEN_2; - }; - */ -OUT:; - // removed the if, since some magic tags misbehavies and send an answer to it. - mifare_classic_halt_ex(NULL); - cmd_send(CMD_ACK, isGen, 0, 0, 0, 0); - // turns off - OnSuccessMagic(); -} - -void OnSuccessMagic(){ - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); -} -void OnErrorMagic(uint8_t reason){ - // ACK, ISOK, reason,0,0,0 - cmd_send(CMD_ACK,0,reason,0,0,0); - OnSuccessMagic(); -} - -void MifareSetMod(uint8_t mod, uint8_t *key) { - uint64_t ui64Key = bytes_to_num(key, 6); - - // variables - uint8_t isOK = 0; - uint8_t uid[10] = {0}; - uint32_t cuid = 0; - struct Crypto1State mpcs = {0, 0}; - struct Crypto1State *pcs = &mpcs; - int respLen = 0; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0}; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - - clear_trace(); - set_tracing(true); - - LED_A_ON(); - LED_B_OFF(); - LED_C_OFF(); - - while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); - break; - } - - if(mifare_classic_auth(pcs, cuid, 0, 0, ui64Key, AUTH_FIRST)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); - break; - } - - if (((respLen = mifare_sendcmd_short(pcs, 1, 0x43, mod, receivedAnswer, receivedAnswerPar, NULL)) != 1) || (receivedAnswer[0] != 0x0a)) { - if (MF_DBGLEVEL >= 1) Dbprintf("SetMod error; response[0]: %hhX, len: %d", receivedAnswer[0], respLen); - break; - } - - if(mifare_classic_halt(pcs, cuid)) { - if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); - break; - } - - isOK = 1; - break; - } - - crypto1_destroy(pcs); - - LED_B_ON(); - cmd_send(CMD_ACK, isOK, 0, 0, 0, 0); - LED_B_OFF(); - - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); -} - -// -// DESFIRE -// -void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain){ - byte_t dataout[12] = {0x00}; - uint8_t uid[10] = {0x00}; - uint32_t cuid = 0; - - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - clear_trace(); - set_tracing(true); - - int len = iso14443a_select_card(uid, NULL, &cuid, true, 0, false); - if(!len) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); - OnError(1); - return; - }; - - if(mifare_desfire_des_auth1(cuid, dataout)){ - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication part1: Fail."); - OnError(4); - return; - } - - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) DbpString("AUTH 1 FINISHED"); - cmd_send(CMD_ACK, 1, cuid, 0, dataout, sizeof(dataout)); -} - -void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain){ - uint32_t cuid = arg0; - uint8_t key[16] = {0x00}; - byte_t dataout[12] = {0x00}; - byte_t isOK = 0; - - memcpy(key, datain, 16); - - isOK = mifare_desfire_des_auth2(cuid, key, dataout); - - if( isOK) { - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication part2: Failed"); - OnError(4); - return; - } - - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) DbpString("AUTH 2 FINISHED"); - - cmd_send(CMD_ACK, isOK, 0, 0, dataout, sizeof(dataout)); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); - set_tracing(false); +//----------------------------------------------------------------------------- +// Merlok - June 2011, 2012 +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 +// Midnitesnake - Dec 2013 +// Andy Davies - Apr 2014 +// Iceman - May 2014,2015,2016 +// +// 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. +//----------------------------------------------------------------------------- +// Routines to support ISO 14443 type A. +//----------------------------------------------------------------------------- + +#include "mifarecmd.h" +#include + +#ifndef HARDNESTED_AUTHENTICATION_TIMEOUT +# define HARDNESTED_AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#endif +#ifndef HARDNESTED_PRE_AUTHENTICATION_LEADTIME +# define HARDNESTED_PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication +#endif + +// send an incomplete dummy response in order to trigger the card's authentication failure timeout +#ifndef CHK_TIMEOUT +# define CHK_TIMEOUT() { \ + ReaderTransmit(&dummy_answer, 1, NULL); \ + uint32_t timeout = GetCountSspClk() + HARDNESTED_AUTHENTICATION_TIMEOUT; \ + while (GetCountSspClk() < timeout) {}; \ + } +#endif + +static uint8_t dummy_answer = 0; + +//----------------------------------------------------------------------------- +// Select, Authenticate, Read a MIFARE tag. +// read block +//----------------------------------------------------------------------------- +void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) +{ + // params + uint8_t blockNo = arg0; + uint8_t keyType = arg1; + uint64_t ui64Key = 0; + ui64Key = bytes_to_num(datain, 6); + + // variables + byte_t isOK = 0; + byte_t dataoutbuf[16] = {0x00}; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + }; + + if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); + break; + }; + + if(mifare_classic_readblock(pcs, cuid, blockNo, dataoutbuf)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Read block error"); + break; + }; + + if(mifare_classic_halt(pcs, cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + }; + + isOK = 1; + break; + } + + crypto1_destroy(pcs); + + if (MF_DBGLEVEL >= 2) DbpString("READ BLOCK FINISHED"); + + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} + +void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes){ + + bool turnOffField = (arg0 == 1); + + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); + OnError(0); + return; + }; + + if(!mifare_ultra_auth(keybytes)){ + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed"); + OnError(1); + return; + } + + if (turnOffField) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + } + cmd_send(CMD_ACK,1,0,0,0,0); +} + +// Arg0 = BlockNo, +// Arg1 = UsePwd bool +// datain = PWD bytes, +void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) +{ + uint8_t blockNo = arg0; + byte_t dataout[16] = {0x00}; + bool useKey = (arg1 == 1); //UL_C + bool usePwd = (arg1 == 2); //UL_EV1/NTAG + + LEDsoff(); + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%02X)",len); + OnError(1); + return; + } + + // UL-C authentication + if ( useKey ) { + uint8_t key[16] = {0x00}; + memcpy(key, datain, sizeof(key) ); + + if ( !mifare_ultra_auth(key) ) { + OnError(1); + return; + } + } + + // UL-EV1 / NTAG authentication + if ( usePwd ) { + uint8_t pwd[4] = {0x00}; + memcpy(pwd, datain, 4); + uint8_t pack[4] = {0,0,0,0}; + if (!mifare_ul_ev1_auth(pwd, pack)) { + OnError(1); + return; + } + } + + if( mifare_ultra_readblock(blockNo, dataout) ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Read block error"); + OnError(2); + return; + } + + if( mifare_ultra_halt() ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Halt error"); + OnError(3); + return; + } + + cmd_send(CMD_ACK,1,0,0,dataout,16); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} + +//----------------------------------------------------------------------------- +// Select, Authenticate, Read a MIFARE tag. +// read sector (data = 4 x 16 bytes = 64 bytes, or 16 x 16 bytes = 256 bytes) +//----------------------------------------------------------------------------- +void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) +{ + // params + uint8_t sectorNo = arg0; + uint8_t keyType = arg1; + uint64_t ui64Key = 0; + ui64Key = bytes_to_num(datain, 6); + + // variables + byte_t isOK = 0; + byte_t dataoutbuf[16 * 16]; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + isOK = 1; + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + isOK = 0; + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + } + + + if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { + isOK = 0; + if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); + } + + for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { + if(mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf + 16 * blockNo)) { + isOK = 0; + if (MF_DBGLEVEL >= 1) Dbprintf("Read sector %2d block %2d error", sectorNo, blockNo); + break; + } + } + + if(mifare_classic_halt(pcs, cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + } + + if (MF_DBGLEVEL >= 2) DbpString("READ SECTOR FINISHED"); + + crypto1_destroy(pcs); + + LED_B_ON(); + cmd_send(CMD_ACK,isOK,0,0,dataoutbuf,16*NumBlocksPerSector(sectorNo)); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} + +// arg0 = blockNo (start) +// arg1 = Pages (number of blocks) +// arg2 = useKey +// datain = KEY bytes +void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) +{ + LEDsoff(); + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + // params + uint8_t blockNo = arg0; + uint16_t blocks = arg1; + bool useKey = (arg2 == 1); //UL_C + bool usePwd = (arg2 == 2); //UL_EV1/NTAG + uint32_t countblocks = 0; + uint8_t *dataout = BigBuf_malloc(CARD_MEMORY_SIZE); + if (dataout == NULL){ + Dbprintf("out of memory"); + OnError(1); + return; + } + + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0, true); + if (!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%d)",len); + OnError(1); + return; + } + + // UL-C authentication + if ( useKey ) { + uint8_t key[16] = {0x00}; + memcpy(key, datain, sizeof(key) ); + + if ( !mifare_ultra_auth(key) ) { + OnError(1); + return; + } + } + + // UL-EV1 / NTAG authentication + if (usePwd) { + uint8_t pwd[4] = {0x00}; + memcpy(pwd, datain, sizeof(pwd)); + uint8_t pack[4] = {0,0,0,0}; + + if (!mifare_ul_ev1_auth(pwd, pack)){ + OnError(1); + return; + } + } + + for (int i = 0; i < blocks; i++){ + if ((i*4) + 4 >= CARD_MEMORY_SIZE) { + Dbprintf("Data exceeds buffer!!"); + break; + } + + len = mifare_ultra_readblock(blockNo + i, dataout + 4 * i); + + if (len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Read block %d error",i); + // if no blocks read - error out + if (i == 0) { + OnError(2); + return; + } else { + //stop at last successful read block and return what we got + break; + } + } else { + countblocks++; + } + } + + len = mifare_ultra_halt(); + if (len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Halt error"); + OnError(3); + return; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Blocks read %d", countblocks); + + countblocks *= 4; + + cmd_send(CMD_ACK, 1, countblocks, BigBuf_max_traceLen(), 0, 0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + BigBuf_free(); + set_tracing(false); +} + +//----------------------------------------------------------------------------- +// Select, Authenticate, Write a MIFARE tag. +// read block +//----------------------------------------------------------------------------- +void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) +{ + // params + uint8_t blockNo = arg0; + uint8_t keyType = arg1; + uint64_t ui64Key = 0; + byte_t blockdata[16] = {0x00}; + + ui64Key = bytes_to_num(datain, 6); + memcpy(blockdata, datain + 10, 16); + + // variables + byte_t isOK = 0; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + }; + + if(mifare_classic_auth(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); + break; + }; + + if(mifare_classic_writeblock(pcs, cuid, blockNo, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + break; + }; + + if(mifare_classic_halt(pcs, cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + }; + + isOK = 1; + break; + } + + crypto1_destroy(pcs); + + if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); + + cmd_send(CMD_ACK,isOK,0,0,0,0); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} + +/* // Command not needed but left for future testing +void MifareUWriteBlockCompat(uint8_t arg0, uint8_t *datain) +{ + uint8_t blockNo = arg0; + byte_t blockdata[16] = {0x00}; + + memcpy(blockdata, datain, 16); + + uint8_t uid[10] = {0x00}; + + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); + + clear_trace(); + set_tracing(true); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + if(!iso14443a_select_card(uid, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + OnError(0); + return; + }; + + if(mifare_ultra_writeblock_compat(blockNo, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(0); + return; }; + + if(mifare_ultra_halt()) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + OnError(0); + return; + }; + + if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); + + cmd_send(CMD_ACK,1,0,0,0,0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} +*/ + +// Arg0 : Block to write to. +// Arg1 : 0 = use no authentication. +// 1 = use 0x1A authentication. +// 2 = use 0x1B authentication. +// datain : 4 first bytes is data to be written. +// : 4/16 next bytes is authentication key. +void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) +{ + uint8_t blockNo = arg0; + bool useKey = (arg1 == 1); //UL_C + bool usePwd = (arg1 == 2); //UL_EV1/NTAG + byte_t blockdata[4] = {0x00}; + + memcpy(blockdata, datain, 4); + + LEDsoff(); + LED_A_ON(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + OnError(0); + return; + }; + + // UL-C authentication + if ( useKey ) { + uint8_t key[16] = {0x00}; + memcpy(key, datain+4, sizeof(key) ); + + if ( !mifare_ultra_auth(key) ) { + OnError(1); + return; + } + } + + // UL-EV1 / NTAG authentication + if (usePwd) { + uint8_t pwd[4] = {0x00}; + memcpy(pwd, datain+4, 4); + uint8_t pack[4] = {0,0,0,0}; + if (!mifare_ul_ev1_auth(pwd, pack)) { + OnError(1); + return; + } + } + + if (mifare_ultra_writeblock(blockNo, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(0); + return; + }; + + if (mifare_ultra_halt()) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + OnError(0); + return; + }; + + if (MF_DBGLEVEL >= 2) DbpString("WRITE BLOCK FINISHED"); + + cmd_send(CMD_ACK,1,0,0,0,0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} + +void MifareUSetPwd(uint8_t arg0, uint8_t *datain){ + + uint8_t pwd[16] = {0x00}; + byte_t blockdata[4] = {0x00}; + + memcpy(pwd, datain, 16); + + LED_A_ON(); LED_B_OFF(); LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + OnError(0); + return; + }; + + blockdata[0] = pwd[7]; + blockdata[1] = pwd[6]; + blockdata[2] = pwd[5]; + blockdata[3] = pwd[4]; + if (mifare_ultra_writeblock( 44, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(44); + return; + }; + + blockdata[0] = pwd[3]; + blockdata[1] = pwd[2]; + blockdata[2] = pwd[1]; + blockdata[3] = pwd[0]; + if (mifare_ultra_writeblock( 45, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(45); + return; + }; + + blockdata[0] = pwd[15]; + blockdata[1] = pwd[14]; + blockdata[2] = pwd[13]; + blockdata[3] = pwd[12]; + if (mifare_ultra_writeblock( 46, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(46); + return; + }; + + blockdata[0] = pwd[11]; + blockdata[1] = pwd[10]; + blockdata[2] = pwd[9]; + blockdata[3] = pwd[8]; + if (mifare_ultra_writeblock( 47, blockdata)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Write block error"); + OnError(47); + return; + }; + + if (mifare_ultra_halt()) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + OnError(0); + return; + }; + + cmd_send(CMD_ACK,1,0,0,0,0); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} + +// Return 1 if the nonce is invalid else return 0 +int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) { + return ((oddparity8((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity8((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ + (oddparity8((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity8((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \ + (oddparity8((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity8((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0; +} + +void MifareAcquireNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain) { + + uint8_t uid[10] = {0x00}; + uint8_t answer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t par[1] = {0x00}; + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + uint32_t cuid = 0; + int16_t isOK = 0; + uint16_t num_nonces = 0; + uint8_t cascade_levels = 0; + uint8_t blockNo = arg0 & 0xff; + uint8_t keyType = (arg0 >> 8) & 0xff; + bool initialize = flags & 0x0001; + bool field_off = flags & 0x0004; + bool have_uid = false; + + LED_A_ON(); + LED_C_OFF(); + + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + if (initialize) + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + LED_C_ON(); + + for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE-4; i += 4 ) { + + // Test if the action was cancelled + if (BUTTON_PRESS()) { + isOK = 2; + field_off = true; + break; + } + + if (!have_uid) { // need a full select cycle to get the uid first + iso14a_card_select_t card_info; + if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); + continue; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); + continue; + } + } + + // Transmit MIFARE_CLASSIC_AUTH + uint8_t dcmd[4] = {0x60 + (keyType & 0x01), blockNo, 0x00, 0x00}; + AddCrc14A(dcmd, 2); + ReaderTransmit(dcmd, sizeof(dcmd), NULL); + int len = ReaderReceive(answer, par); + + // wait for the card to become ready again + CHK_TIMEOUT(); + + if (len != 4) { + if (MF_DBGLEVEL >= 2) Dbprintf("AcquireNonces: Auth1 error"); + continue; + } + + num_nonces++; + + // Save the tag nonce (nt) + buf[i] = answer[0]; + buf[i+1] = answer[1]; + buf[i+2] = answer[2]; + buf[i+3] = answer[3]; + } + + LED_C_OFF(); + LED_B_ON(); + cmd_send(CMD_ACK, isOK, cuid, num_nonces-1, buf, sizeof(buf)); + LED_B_OFF(); + + if (MF_DBGLEVEL >= 3) DbpString("AcquireNonces finished"); + + if (field_off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + } +} + +//----------------------------------------------------------------------------- +// acquire encrypted nonces in order to perform the attack described in +// Carlo Meijer, Roel Verdult, "Ciphertext-only Cryptanalysis on Hardened +// Mifare Classic Cards" in Proceedings of the 22nd ACM SIGSAC Conference on +// Computer and Communications Security, 2015 +//----------------------------------------------------------------------------- +void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain) { + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint8_t uid[10] = {0x00}; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t par_enc[1] = {0x00}; + uint8_t buf[USB_CMD_DATA_SIZE] = {0x00}; + + uint64_t ui64Key = bytes_to_num(datain, 6); + uint32_t cuid = 0; + int16_t isOK = 0; + uint16_t num_nonces = 0; + uint8_t nt_par_enc = 0; + uint8_t cascade_levels = 0; + uint8_t blockNo = arg0 & 0xff; + uint8_t keyType = (arg0 >> 8) & 0xff; + uint8_t targetBlockNo = arg1 & 0xff; + uint8_t targetKeyType = (arg1 >> 8) & 0xff; + bool initialize = flags & 0x0001; + bool slow = flags & 0x0002; + bool field_off = flags & 0x0004; + bool have_uid = false; + + LED_A_ON(); + LED_C_OFF(); + + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(false); + + if (initialize) + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + LED_C_ON(); + + for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 9; ) { + + // Test if the action was cancelled + if(BUTTON_PRESS()) { + isOK = 2; + field_off = true; + break; + } + + if (!have_uid) { // need a full select cycle to get the uid first + iso14a_card_select_t card_info; + if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (ALL)"); + continue; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_fast_select_card(uid, cascade_levels)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); + continue; + } + } + + if (slow) + SpinDelayUs(HARDNESTED_PRE_AUTHENTICATION_LEADTIME); + + uint32_t nt1; + if (mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, NULL)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth1 error"); + continue; + } + + // nested authentication + uint16_t len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par_enc, NULL); + + // wait for the card to become ready again + CHK_TIMEOUT(); + + if (len != 4) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth2 error len=%d", len); + continue; + } + + num_nonces++; + if (num_nonces % 2) { + memcpy(buf+i, receivedAnswer, 4); + nt_par_enc = par_enc[0] & 0xf0; + } else { + nt_par_enc |= par_enc[0] >> 4; + memcpy(buf+i+4, receivedAnswer, 4); + memcpy(buf+i+8, &nt_par_enc, 1); + i += 9; + } + } + + LED_C_OFF(); + crypto1_destroy(pcs); + LED_B_ON(); + cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); + LED_B_OFF(); + + if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished"); + + if (field_off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); + } +} + + +//----------------------------------------------------------------------------- +// MIFARE nested authentication. +// +//----------------------------------------------------------------------------- +void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *datain) +{ + // params + uint8_t blockNo = arg0 & 0xff; + uint8_t keyType = (arg0 >> 8) & 0xff; + uint8_t targetBlockNo = arg1 & 0xff; + uint8_t targetKeyType = (arg1 >> 8) & 0xff; + uint64_t ui64Key = 0; + + ui64Key = bytes_to_num(datain, 6); + + // variables + uint16_t rtr, i, j, len; + uint16_t davg = 0; + static uint16_t dmin, dmax; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0, nt1, nt2, nttmp, nttest, ks1; + uint8_t par[1] = {0x00}; + uint32_t target_nt[2] = {0x00}, target_ks[2] = {0x00}; + + uint8_t par_array[4] = {0x00}; + uint16_t ncount = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + + uint32_t auth1_time, auth2_time; + static uint16_t delta_time = 0; + + LED_A_ON(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // free eventually allocated BigBuf memory + BigBuf_free(); BigBuf_Clear_ext(false); + + if (calibrate) clear_trace(); + set_tracing(true); + + // statistics on nonce distance + int16_t isOK = 0; + #define NESTED_MAX_TRIES 12 + uint16_t unsuccessfull_tries = 0; + if (calibrate) { // for first call only. Otherwise reuse previous calibration + LED_B_ON(); + WDT_HIT(); + + davg = dmax = 0; + dmin = 2000; + delta_time = 0; + + for (rtr = 0; rtr < 17; rtr++) { + + // Test if the action was cancelled + if(BUTTON_PRESS()) { + isOK = -2; + break; + } + + // prepare next select. No need to power down the card. + if(mifare_classic_halt(pcs, cuid)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); + rtr--; + continue; + } + + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Can't select card"); + rtr--; + continue; + }; + + auth1_time = 0; + if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); + rtr--; + continue; + }; + auth2_time = (delta_time) ? auth1_time + delta_time : 0; + + if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_NESTED, &nt2, &auth2_time)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error"); + rtr--; + continue; + }; + + nttmp = prng_successor(nt1, 100); //NXP Mifare is typical around 840,but for some unlicensed/compatible mifare card this can be 160 + for (i = 101; i < 1200; i++) { + nttmp = prng_successor(nttmp, 1); + if (nttmp == nt2) break; + } + + if (i != 1200) { + if (rtr != 0) { + davg += i; + dmin = MIN(dmin, i); + dmax = MAX(dmax, i); + } + else { + delta_time = auth2_time - auth1_time + 32; // allow some slack for proper timing + } + if (MF_DBGLEVEL >= 3) Dbprintf("Nested: calibrating... ntdist=%d", i); + } else { + unsuccessfull_tries++; + if (unsuccessfull_tries > NESTED_MAX_TRIES) { // card isn't vulnerable to nested attack (random numbers are not predictable) + isOK = -3; + } + } + } + + davg = (davg + (rtr - 1)/2) / (rtr - 1); + + if (MF_DBGLEVEL >= 3) Dbprintf("rtr=%d isOK=%d min=%d max=%d avg=%d, delta_time=%d", rtr, isOK, dmin, dmax, davg, delta_time); + + dmin = davg - 2; + dmax = davg + 2; + + LED_B_OFF(); + } +// ------------------------------------------------------------------------------------------------- + + LED_C_ON(); + + // get crypted nonces for target sector + for(i=0; i < 2 && !isOK; i++) { // look for exactly two different nonces + + target_nt[i] = 0; + while(target_nt[i] == 0) { // continue until we have an unambiguous nonce + + // prepare next select. No need to power down the card. + if(mifare_classic_halt(pcs, cuid)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Halt error"); + continue; + } + + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Can't select card"); + continue; + }; + + auth1_time = 0; + if(mifare_classic_authex(pcs, cuid, blockNo, keyType, ui64Key, AUTH_FIRST, &nt1, &auth1_time)) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth1 error"); + continue; + }; + + // nested authentication + auth2_time = auth1_time + delta_time; + + len = mifare_sendcmd_short(pcs, AUTH_NESTED, 0x60 + (targetKeyType & 0x01), targetBlockNo, receivedAnswer, par, &auth2_time); + if (len != 4) { + if (MF_DBGLEVEL >= 2) Dbprintf("Nested: Auth2 error len=%d", len); + continue; + }; + + nt2 = bytes_to_num(receivedAnswer, 4); + if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: Testing nt1=%08x nt2enc=%08x nt2par=%02x", i+1, nt1, nt2, par[0]); + + // Parity validity check + for (j = 0; j < 4; j++) { + par_array[j] = (oddparity8(receivedAnswer[j]) != ((par[0] >> (7-j)) & 0x01)); + } + + ncount = 0; + nttest = prng_successor(nt1, dmin - 1); + for (j = dmin; j < dmax + 1; j++) { + nttest = prng_successor(nttest, 1); + ks1 = nt2 ^ nttest; + + if (valid_nonce(nttest, nt2, ks1, par_array)){ + if (ncount > 0) { // we are only interested in disambiguous nonces, try again + if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (ambigous), ntdist=%d", i+1, j); + target_nt[i] = 0; + break; + } + target_nt[i] = nttest; + target_ks[i] = ks1; + ncount++; + if (i == 1 && target_nt[1] == target_nt[0]) { // we need two different nonces + target_nt[i] = 0; + if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#2: dismissed (= nonce#1), ntdist=%d", j); + break; + } + if (MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: valid, ntdist=%d", i+1, j); + } + } + if (target_nt[i] == 0 && j == dmax+1 && MF_DBGLEVEL >= 3) Dbprintf("Nonce#%d: dismissed (all invalid)", i+1); + } + } + + LED_C_OFF(); + + crypto1_destroy(pcs); + + uint8_t buf[4 + 4 * 4] = {0}; + memcpy(buf, &cuid, 4); + memcpy(buf+4, &target_nt[0], 4); + memcpy(buf+8, &target_ks[0], 4); + memcpy(buf+12, &target_nt[1], 4); + memcpy(buf+16, &target_ks[1], 4); + + LED_B_ON(); + cmd_send(CMD_ACK, isOK, 0, targetBlockNo + (targetKeyType * 0x100), buf, sizeof(buf)); + LED_B_OFF(); + + if (MF_DBGLEVEL >= 3) DbpString("NESTED FINISHED"); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} + +//----------------------------------------------------------------------------- +// MIFARE check keys. key count up to 85. +// +//----------------------------------------------------------------------------- +typedef struct sector_t { + uint8_t keyA[6]; + uint8_t keyB[6]; +} sector_t; + +typedef struct chk_t { + uint64_t key; + uint32_t cuid; + uint8_t cl; + uint8_t block; + uint8_t keyType; + uint8_t *uid; + struct Crypto1State *pcs; +} chk_t; + +// checks one key. +// fast select, tries 5 times to select +// +// return: +// 2 = failed to select. +// 1 = wrong key +// 0 = correct key +uint8_t chkKey( struct chk_t *c ) { + uint8_t i = 0, res = 2; + while( i < 5 ) { + // this part is from Piwi's faster nonce collecting part in Hardnested. + // assume: fast select + if (!iso14443a_fast_select_card(c->uid, c->cl)) { + ++i; + continue; + } + res = mifare_classic_authex(c->pcs, c->cuid, c->block, c->keyType, c->key, AUTH_FIRST, NULL, NULL); + + CHK_TIMEOUT(); + + // if successfull auth, send HALT + // if ( !res ) + // mifare_classic_halt_ex(c->pcs); + break; + } + return res; +} + +uint8_t chkKey_readb(struct chk_t *c, uint8_t *keyb) { + + if (!iso14443a_fast_select_card(c->uid, c->cl)) + return 2; + + if ( mifare_classic_authex(c->pcs, c->cuid, c->block, 0, c->key, AUTH_FIRST, NULL, NULL) ) + return 1; + + uint8_t data[16] = {0x00}; + uint8_t res = mifare_classic_readblock(c->pcs, c->cuid, c->block, data); + + // successful read + if ( !res ) { + // data was something else than zeros. + if ( memcmp(data+10, "\x00\x00\x00\x00\x00\x00", 6) != 0) { + memcpy(keyb, data+10, 6); + res = 0; + } else { + res = 3; + } + mifare_classic_halt_ex(c->pcs); + } + return res; +} + +void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + for (uint8_t s = 0; s < *sectorcnt; s++) { + + // skip already found A keys + if ( found[(s*2)] ) + continue; + + c->block = FirstBlockOfSector( s ); + if ( chkKey( c ) == 0 ) { + num_to_bytes(c->key, 6, k_sector[s].keyA); + found[(s*2)] = 1; + ++*foundkeys; + + if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan A found (%d)", c->block); + } + } +} + +void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + for (uint8_t s = 0; s < *sectorcnt; s++) { + + // skip already found B keys + if ( found[(s*2)+1] ) + continue; + + c->block = FirstBlockOfSector( s ); + if ( chkKey( c ) == 0 ) { + num_to_bytes(c->key, 6, k_sector[s].keyB); + found[(s*2)+1] = 1; + ++*foundkeys; + + if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Scan B found (%d)", c->block); + } + } +} + +// loop all A keys, +// when A is found but not B, try to read B. +void chkKey_loopBonly(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) { + + // read Block B, if A is found. + for (uint8_t s = 0; s < *sectorcnt; ++s) { + + if ( found[(s*2)] && found[(s*2)+1] ) + continue; + + c->block = (FirstBlockOfSector( s ) + NumBlocksPerSector( s ) - 1); + + // A but not B + if ( found[(s*2)] && !found[(s*2)+1] ){ + c->key = bytes_to_num(k_sector[s].keyA, 6); + uint8_t status = chkKey_readb(c, k_sector[s].keyB); + if ( status == 0 ){ + found[(s*2)+1] = 1; + ++*foundkeys; + + if (MF_DBGLEVEL >= 3) Dbprintf("ChkKeys_fast: Reading B found (%d)", c->block); + + // try quick find all B? + // assume: keys comes in groups. Find one B, test against all B. + c->key = bytes_to_num( k_sector[s].keyB, 6); + c->keyType = 1; + chkKey_scanB(c, k_sector, found, sectorcnt, foundkeys); + } + } + } +} + + + +// get Chunks of keys, to test authentication against card. +// arg0 = antal sectorer +// arg0 = first time +// arg1 = clear trace +// arg2 = antal nycklar i keychunk +// datain = keys as array +void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain) { + + // first call or + uint8_t sectorcnt = arg0 & 0xFF; // 16; + uint8_t firstchunk = (arg0 >> 8) & 0xF; + uint8_t lastchunk = (arg0 >> 12) & 0xF; + uint8_t strategy = arg1 & 0xFF; + uint8_t use_flashmem = (arg1 >> 8) & 0xFF; + uint16_t keyCount = arg2 & 0xFF; + uint8_t status = 0; + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + struct chk_t chk_data; + + uint8_t allkeys = sectorcnt << 1; + + static uint32_t cuid = 0; + static uint8_t cascade_levels = 0; + static uint8_t foundkeys = 0; + static sector_t k_sector[80]; + static uint8_t found[80]; + static uint8_t *uid; + +#ifdef WITH_FLASH + if ( use_flashmem ) { + BigBuf_free(); + uint16_t isok = 0; + uint8_t size[2] = {0x00, 0x00}; + isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET, size, 2); + if ( isok != 2 ) + goto OUT; + + keyCount = size[1] << 8 | size[0]; + + if ( keyCount == 0 && keyCount == 0xFFFF) + goto OUT; + + datain = BigBuf_malloc( keyCount * 6); + if (datain == NULL ) + goto OUT; + + isok = Flash_ReadData(DEFAULT_MF_KEYS_OFFSET+2, datain, keyCount * 6); + if ( isok != keyCount * 6 ) + goto OUT; + + } +#endif + + if (uid == NULL || firstchunk) { + uid = BigBuf_malloc(10); + if (uid == NULL ) + goto OUT; + } + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + LEDsoff(); + LED_A_ON(); + + if ( firstchunk ) { + clear_trace(); + set_tracing(false); + + memset(k_sector, 0x00, 480+10); + memset(found, 0x00, sizeof(found)); + foundkeys = 0; + + iso14a_card_select_t card_info; + if ( !iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys_fast: Can't select card (ALL)"); + goto OUT; + } + + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + + CHK_TIMEOUT(); + } + + // set check struct. + chk_data.uid = uid; + chk_data.cuid = cuid; + chk_data.cl = cascade_levels; + chk_data.pcs = pcs; + chk_data.block = 0; + + // keychunk loop - depth first one sector. + if ( strategy == 1 || use_flashmem) { + + uint8_t newfound = foundkeys; + + uint16_t lastpos = 0; + uint16_t s_point = 0; + // Sector main loop + // keep track of how many sectors on card. + for (uint8_t s = 0; s < sectorcnt; ++s) { + + if ( found[(s*2)] && found[(s*2)+1] ) + continue; + + for (uint16_t i = s_point; i < keyCount; ++i) { + + //if ( i % 100 == 0) Dbprintf("ChkKeys_fast: sector %d | checking %d | %d found | s_point %d", s, i, foundkeys, s_point); + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) { + goto OUT; + } + + // found all keys? + if ( foundkeys == allkeys ) + goto OUT; + + WDT_HIT(); + + // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector + chk_data.block = FirstBlockOfSector( s ); + + // new key + chk_data.key = bytes_to_num(datain + i * 6, 6); + + // skip already found A keys + if( !found[(s*2)] ) { + chk_data.keyType = 0; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyA, datain + i * 6, 6); + found[(s*2)] = 1; + ++foundkeys; + + chkKey_scanA(&chk_data, k_sector, found, §orcnt, &foundkeys); + + // read Block B, if A is found. + chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys); + + chk_data.keyType = 1; + chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); + + chk_data.keyType = 0; + chk_data.block = FirstBlockOfSector( s ); + + if ( use_flashmem ) { + if ( lastpos != i && lastpos != 0) { + if ( i - lastpos < 0xF) { + s_point = i & 0xFFF0; + } + } else { + lastpos = i; + } + } + } + } + + // skip already found B keys + if( !found[(s*2)+1] ) { + chk_data.keyType = 1; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyB, datain + i * 6, 6); + found[(s*2)+1] = 1; + ++foundkeys; + + chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); + + if ( use_flashmem ) { + if ( lastpos != i && lastpos != 0) { + + if ( i - lastpos < 0xF) + s_point = i & 0xFFF0; + } else { + lastpos = i; + } + } + } + } + + if ( found[(s*2)] && found[(s*2)+1] ) + break; + + } // end keys test loop - depth first + + // assume1. if no keys found in first sector, get next keychunk from client + if ( !use_flashmem && (newfound-foundkeys == 0) ) + goto OUT; + + } // end loop - sector + } // end strategy 1 + + if ( foundkeys == allkeys ) + goto OUT; + + if ( strategy == 2 || use_flashmem ) { + + // Keychunk loop + for (uint16_t i = 0; i < keyCount; i++) { + + // Allow button press / usb cmd to interrupt device + if (BUTTON_PRESS() && !usb_poll_validate_length()) break; + + // found all keys? + if ( foundkeys == allkeys ) + goto OUT; + + WDT_HIT(); + + // new key + chk_data.key = bytes_to_num(datain + i * 6, 6); + + // Sector main loop + // keep track of how many sectors on card. + for (uint8_t s = 0; s < sectorcnt; ++s) { + + if ( found[(s*2)] && found[(s*2)+1] ) continue; + + // found all keys? + if ( foundkeys == allkeys ) + goto OUT; + + // assume: block0,1,2 has more read rights in accessbits than the sectortrailer. authenticating against block0 in each sector + chk_data.block = FirstBlockOfSector( s ); + + // skip already found A keys + if( !found[(s*2)] ) { + chk_data.keyType = 0; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyA, datain + i * 6, 6); + found[(s*2)] = 1; + ++foundkeys; + + chkKey_scanA( &chk_data, k_sector, found, §orcnt, &foundkeys); + + // read Block B, if A is found. + chkKey_loopBonly( &chk_data, k_sector, found, §orcnt, &foundkeys); + + chk_data.block = FirstBlockOfSector( s ); + } + } + + // skip already found B keys + if( !found[(s*2)+1] ) { + chk_data.keyType = 1; + status = chkKey( &chk_data); + if ( status == 0 ) { + memcpy(k_sector[s].keyB, datain + i * 6, 6); + found[(s*2)+1] = 1; + ++foundkeys; + + chkKey_scanB(&chk_data, k_sector, found, §orcnt, &foundkeys); + } + } + } // end loop sectors + } // end loop keys + } // end loop strategy 2 +OUT: + LEDsoff(); + + crypto1_destroy(pcs); + + // All keys found, send to client, or last keychunk from client + if (foundkeys == allkeys || lastchunk ) { + + uint64_t foo = 0; + for (uint8_t m = 0; m < 64; m++) { + foo |= ((uint64_t)(found[m] & 1) << m); + } + + uint16_t bar = 0; + uint8_t j = 0; + for (uint8_t m=64; m < sizeof(found); m++) { + bar |= ((uint16_t)(found[m] & 1) << j++); + } + + uint8_t *tmp = BigBuf_malloc(480+10); + memcpy(tmp, k_sector, sectorcnt * sizeof(sector_t) ); + num_to_bytes(foo, 8, tmp+480); + tmp[488] = bar & 0xFF; + tmp[489] = bar >> 8 & 0xFF; + + cmd_send(CMD_ACK, foundkeys, 0, 0, tmp, 480+10); + + set_tracing(false); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + BigBuf_free(); BigBuf_Clear_ext(false); + } else { + // partial/none keys found + cmd_send(CMD_ACK, foundkeys, 0, 0, 0, 0); + } +} + +void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) { + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + uint8_t uid[10] = {0x00}; + + uint64_t key = 0; + uint32_t cuid = 0; + int i, res; + uint8_t blockNo = arg0 & 0xFF; + uint8_t keyType = (arg0 >> 8) & 0xFF; + uint8_t keyCount = arg2; + uint8_t cascade_levels = 0; + uint8_t isOK = 0; + bool have_uid = false; + bool clearTrace = arg1 & 0xFF; + + LEDsoff(); + LED_A_ON(); + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + if (clearTrace) + clear_trace(); + + set_tracing(true); + + for (i = 0; i < keyCount; i++) { + + // Iceman: use piwi's faster nonce collecting part in hardnested. + if (!have_uid) { // need a full select cycle to get the uid first + iso14a_card_select_t card_info; + if (!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (ALL)"); + --i; // try same key once again + continue; + } + switch (card_info.uidlen) { + case 4 : cascade_levels = 1; break; + case 7 : cascade_levels = 2; break; + case 10: cascade_levels = 3; break; + default: break; + } + have_uid = true; + } else { // no need for anticollision. We can directly select the card + if (!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card (UID)"); + --i; // try same key once again + continue; + } + } + + key = bytes_to_num(datain + i * 6, 6); + res = mifare_classic_auth(pcs, cuid, blockNo, keyType, key, AUTH_FIRST); + + CHK_TIMEOUT(); + + if (res) + continue; + + isOK = 1; + break; + } + + LED_B_ON(); + cmd_send(CMD_ACK, isOK, 0, 0, datain + i * 6, 6); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + + set_tracing(false); + crypto1_destroy(pcs); +} + +//----------------------------------------------------------------------------- +// MIFARE commands set debug level +// +//----------------------------------------------------------------------------- +void MifareSetDbgLvl(uint16_t arg0){ + MF_DBGLEVEL = arg0; + Dbprintf("Debug level: %d", MF_DBGLEVEL); +} + +//----------------------------------------------------------------------------- +// Work with emulator memory +// +// Note: we call FpgaDownloadAndGo(FPGA_BITSTREAM_HF) here although FPGA is not +// involved in dealing with emulator memory. But if it is called later, it might +// destroy the Emulator Memory. +//----------------------------------------------------------------------------- + +void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + emlClearMem(); +} + +void MifareEMemSet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + if (arg2==0) arg2 = 16; // backwards compat... default bytewidth + emlSetMem_xt(datain, arg0, arg1, arg2); // data, block num, blocks count, block byte width +} + +void MifareEMemGet(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ + FpgaDownloadAndGo(FPGA_BITSTREAM_HF); + byte_t buf[USB_CMD_DATA_SIZE] = {0x00}; + emlGetMem(buf, arg0, arg1); // data, block num, blocks count (max 4) + + LED_B_ON(); + cmd_send(CMD_ACK,arg0,arg1,0,buf,USB_CMD_DATA_SIZE); + LED_B_OFF(); +} + +//----------------------------------------------------------------------------- +// Load a card into the emulator memory +// +//----------------------------------------------------------------------------- +void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain){ + uint8_t numSectors = arg0; + uint8_t keyType = arg1; + uint64_t ui64Key = 0; + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + + // variables + byte_t dataoutbuf[16] = {0x00}; + byte_t dataoutbuf2[16] = {0x00}; + uint8_t uid[10] = {0x00}; + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + bool isOK = true; + + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + isOK = false; + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + } + + for (uint8_t sectorNo = 0; isOK && sectorNo < numSectors; sectorNo++) { + ui64Key = emlGetKey(sectorNo, keyType); + if (sectorNo == 0){ + if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_FIRST)) { + isOK = false; + if (MF_DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth error", sectorNo); + break; + } + } else { + if(isOK && mifare_classic_auth(pcs, cuid, FirstBlockOfSector(sectorNo), keyType, ui64Key, AUTH_NESTED)) { + isOK = false; + if (MF_DBGLEVEL >= 1) Dbprintf("Sector[%2d]. Auth nested error", sectorNo); + break; + } + } + + for (uint8_t blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { + if(isOK && mifare_classic_readblock(pcs, cuid, FirstBlockOfSector(sectorNo) + blockNo, dataoutbuf)) { + isOK = false; + if (MF_DBGLEVEL >= 1) Dbprintf("Error reading sector %2d block %2d", sectorNo, blockNo); + break; + } + if (isOK) { + if (blockNo < NumBlocksPerSector(sectorNo) - 1) { + emlSetMem(dataoutbuf, FirstBlockOfSector(sectorNo) + blockNo, 1); + } else { // sector trailer, keep the keys, set only the AC + emlGetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); + memcpy(&dataoutbuf2[6], &dataoutbuf[6], 4); + emlSetMem(dataoutbuf2, FirstBlockOfSector(sectorNo) + blockNo, 1); + } + } + } + + } + + if(mifare_classic_halt(pcs, cuid)) + if (MF_DBGLEVEL >= 1) + Dbprintf("Halt error"); + + // ----------------------------- crypto1 destroy + crypto1_destroy(pcs); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + + if (MF_DBGLEVEL >= 2) DbpString("EMUL FILL SECTORS FINISHED"); + + set_tracing(false); +} + + +//----------------------------------------------------------------------------- +// Work with "magic Chinese" card (email him: ouyangweidaxian@live.cn) +// +// PARAMS - workFlags +// bit 0 - need get UID +// bit 1 - need wupC +// bit 2 - need HALT after sequence +// bit 3 - need turn on FPGA before sequence +// bit 4 - need turn off FPGA +// bit 5 - need to set datain instead of issuing USB reply (called via ARM for StandAloneMode14a) +// bit 6 - wipe tag. +//----------------------------------------------------------------------------- +// magic uid card generation 1 commands +uint8_t wupC1[] = { MIFARE_MAGICWUPC1 }; +uint8_t wupC2[] = { MIFARE_MAGICWUPC2 }; +uint8_t wipeC[] = { MIFARE_MAGICWIPEC }; + +void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain){ + + // params + uint8_t workFlags = arg0; + uint8_t blockNo = arg1; + + // detect 1a/1b + bool is1b = false; + + // variables + bool isOK = false; //assume we will get an error + uint8_t errormsg = 0x00; + uint8_t uid[10] = {0x00}; + uint8_t data[18] = {0x00}; + uint32_t cuid = 0; + + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + if (workFlags & MAGIC_INIT) { + LED_A_ON(); + LED_B_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + } + + //loop doesn't loop just breaks out if error + while (true) { + // read UID and return to client with write + if (workFlags & MAGIC_UID) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); + errormsg = MAGIC_UID; + } + mifare_classic_halt_ex(NULL); + break; + } + + // wipe tag, fill it with zeros + if (workFlags & MAGIC_WIPE){ + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); + errormsg = MAGIC_WIPE; + break; + } + + ReaderTransmit(wipeC, sizeof(wipeC), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wipeC error"); + errormsg = MAGIC_WIPE; + break; + } + + mifare_classic_halt_ex(NULL); + } + + // write block + if (workFlags & MAGIC_WUPC) { + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); + errormsg = MAGIC_WUPC; + break; + } + + if ( !is1b ) { + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]"); + is1b = true; + continue; + } + } + } + + if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("write block send command error"); + errormsg = 4; + break; + } + + memcpy(data, datain, 16); + AddCrc14A(data, 16); + + ReaderTransmit(data, sizeof(data), NULL); + if ((ReaderReceive(receivedAnswer, receivedAnswerPar) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("write block send data error"); + errormsg = 0; + break; + } + + if (workFlags & MAGIC_HALT) + mifare_classic_halt_ex(NULL); + + isOK = true; + break; + + } // end while + + if (isOK ) + cmd_send(CMD_ACK,1,0,0,uid,sizeof(uid)); + else + OnErrorMagic(errormsg); + + if (workFlags & MAGIC_OFF) + OnSuccessMagic(); +} + +void MifareCGetBlock(uint32_t arg0, uint32_t arg1, uint8_t *datain){ + + uint8_t workFlags = arg0; + uint8_t blockNo = arg1; + uint8_t errormsg = 0x00; + bool isOK = false; //assume we will get an error + + // detect 1a/1b + bool is1b = false; + + // variables + uint8_t data[MAX_MIFARE_FRAME_SIZE]; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + memset(data, 0x00, sizeof(data)); + + if (workFlags & MAGIC_INIT) { + LED_A_ON(); + LED_B_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + } + + //loop doesn't loop just breaks out if error or done + while (true) { + if (workFlags & MAGIC_WUPC) { + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("wupC1 error"); + errormsg = MAGIC_WUPC; + break; + } + + if ( !is1b ) { + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(receivedAnswer, receivedAnswerPar) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Assuming Magic Gen 1B tag. [wupC2 failed]"); + is1b = true; + continue; + } + } + } + + // read block + if ((mifare_sendcmd_short(NULL, 0, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL) != 18)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("read block send command error"); + errormsg = 0; + break; + } + + memcpy(data, receivedAnswer, sizeof(data)); + + // send HALT + if (workFlags & MAGIC_HALT) + mifare_classic_halt_ex(NULL); + + isOK = true; + break; + } + // if MAGIC_DATAIN, the data stays on device side. + if (workFlags & MAGIC_DATAIN) { + if (isOK) + memcpy(datain, data, sizeof(data)); + } else { + if (isOK) + cmd_send(CMD_ACK,1,0,0,data,sizeof(data)); + else + OnErrorMagic(errormsg); + } + + if (workFlags & MAGIC_OFF) + OnSuccessMagic(); +} + +void MifareCIdent(){ + #define GEN_1A 1 + #define GEN_1B 2 + #define GEN_2 4 + // variables + uint8_t isGen = 0; + uint8_t rec[1] = {0x00}; + uint8_t recpar[1] = {0x00}; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + // Generation 1 test + ReaderTransmitBitsPar(wupC1, 7, NULL, NULL); + if(!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { + goto TEST2; + }; + isGen = GEN_1B; + + ReaderTransmit(wupC2, sizeof(wupC2), NULL); + if(!ReaderReceive(rec, recpar) || (rec[0] != 0x0a)) { + goto OUT; + }; + isGen = GEN_1A; + goto OUT; + +TEST2:; +/* + // Generation 2 test + + // halt previous. + mifare_classic_halt(NULL, 0); + + //select + if (!iso14443a_select_card(NULL, NULL, NULL, true, 0, true)) { + goto OUT; + }; + + // MIFARE_CLASSIC_WRITEBLOCK 0xA0 + // ACK 0x0a + uint16_t len = mifare_sendcmd_short(null, 1, 0xA0, 0, rec, recpar, NULL); + if ((len != 1) || (rec[0] != 0x0A)) { + isGen = GEN_2; + }; + */ +OUT:; + // removed the if, since some magic tags misbehavies and send an answer to it. + mifare_classic_halt_ex(NULL); + cmd_send(CMD_ACK, isGen, 0, 0, 0, 0); + // turns off + OnSuccessMagic(); +} + +void OnSuccessMagic(){ + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); +} +void OnErrorMagic(uint8_t reason){ + // ACK, ISOK, reason,0,0,0 + cmd_send(CMD_ACK,0,reason,0,0,0); + OnSuccessMagic(); +} + +void MifareSetMod(uint8_t mod, uint8_t *key) { + uint64_t ui64Key = bytes_to_num(key, 6); + + // variables + uint8_t isOK = 0; + uint8_t uid[10] = {0}; + uint32_t cuid = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs = &mpcs; + int respLen = 0; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0}; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + clear_trace(); + set_tracing(true); + + LED_A_ON(); + LED_B_OFF(); + LED_C_OFF(); + + while (true) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0, true)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); + break; + } + + if(mifare_classic_auth(pcs, cuid, 0, 0, ui64Key, AUTH_FIRST)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Auth error"); + break; + } + + if (((respLen = mifare_sendcmd_short(pcs, 1, 0x43, mod, receivedAnswer, receivedAnswerPar, NULL)) != 1) || (receivedAnswer[0] != 0x0a)) { + if (MF_DBGLEVEL >= 1) Dbprintf("SetMod error; response[0]: %hhX, len: %d", receivedAnswer[0], respLen); + break; + } + + if(mifare_classic_halt(pcs, cuid)) { + if (MF_DBGLEVEL >= 1) Dbprintf("Halt error"); + break; + } + + isOK = 1; + break; + } + + crypto1_destroy(pcs); + + LED_B_ON(); + cmd_send(CMD_ACK, isOK, 0, 0, 0, 0); + LED_B_OFF(); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} + +// +// DESFIRE +// +void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain){ + byte_t dataout[12] = {0x00}; + uint8_t uid[10] = {0x00}; + uint32_t cuid = 0; + + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + clear_trace(); + set_tracing(true); + + int len = iso14443a_select_card(uid, NULL, &cuid, true, 0, false); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); + OnError(1); + return; + }; + + if(mifare_desfire_des_auth1(cuid, dataout)){ + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication part1: Fail."); + OnError(4); + return; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) DbpString("AUTH 1 FINISHED"); + cmd_send(CMD_ACK, 1, cuid, 0, dataout, sizeof(dataout)); +} + +void Mifare_DES_Auth2(uint32_t arg0, uint8_t *datain){ + uint32_t cuid = arg0; + uint8_t key[16] = {0x00}; + byte_t dataout[12] = {0x00}; + byte_t isOK = 0; + + memcpy(key, datain, 16); + + isOK = mifare_desfire_des_auth2(cuid, key, dataout); + + if( isOK) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication part2: Failed"); + OnError(4); + return; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) DbpString("AUTH 2 FINISHED"); + + cmd_send(CMD_ACK, isOK, 0, 0, dataout, sizeof(dataout)); + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + set_tracing(false); } \ No newline at end of file diff --git a/armsrc/mifarecmd.h b/armsrc/mifarecmd.h index d73b124cb..595d46395 100644 --- a/armsrc/mifarecmd.h +++ b/armsrc/mifarecmd.h @@ -1,28 +1,28 @@ -//----------------------------------------------------------------------------- -// Merlok - June 2011 -// Gerhard de Koning Gans - May 2008 -// Hagen Fritsch - June 2010 -// -// 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. -//----------------------------------------------------------------------------- -// Routines to support ISO 14443 type A. -//----------------------------------------------------------------------------- - -#ifndef __MIFARECMD_H -#define __MIFARECMD_H - -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "iso14443crc.h" -#include "iso14443a.h" -#include "crapto1/crapto1.h" -#include "mifareutil.h" -#include "common.h" -#include "crc.h" -#include "protocols.h" -#include "parity.h" +//----------------------------------------------------------------------------- +// Merlok - June 2011 +// Gerhard de Koning Gans - May 2008 +// Hagen Fritsch - June 2010 +// +// 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. +//----------------------------------------------------------------------------- +// Routines to support ISO 14443 type A. +//----------------------------------------------------------------------------- + +#ifndef __MIFARECMD_H +#define __MIFARECMD_H + +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "string.h" +#include "iso14443crc.h" +#include "iso14443a.h" +#include "crapto1/crapto1.h" +#include "mifareutil.h" +#include "common.h" +#include "crc.h" +#include "protocols.h" +#include "parity.h" #endif \ No newline at end of file diff --git a/armsrc/mifaresniff.c b/armsrc/mifaresniff.c index 3e6255354..e832c76cf 100644 --- a/armsrc/mifaresniff.c +++ b/armsrc/mifaresniff.c @@ -1,324 +1,324 @@ -//----------------------------------------------------------------------------- -// Merlok - 2012 -// -// 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. -//----------------------------------------------------------------------------- -// Routines to support mifare classic sniffer. -//----------------------------------------------------------------------------- - -#include "mifaresniff.h" - -//static int sniffState = SNF_INIT; -static uint8_t sniffUIDType = 0; -static uint8_t sniffUID[10] = {0,0,0,0,0,0,0,0,0,0}; -static uint8_t sniffATQA[2] = {0,0}; -static uint8_t sniffSAK = 0; -static uint8_t sniffBuf[17]; -static uint32_t timerData = 0; - -//----------------------------------------------------------------------------- -// MIFARE sniffer. -// -// if no activity for 2sec, it sends the collected data to the client. -//----------------------------------------------------------------------------- -// "hf mf sniff" -void RAMFUNC SniffMifare(uint8_t param) { - // param: - // bit 0 - trigger from first card answer - // bit 1 - trigger from first reader 7-bit request - - // C(red) A(yellow) B(green) - LEDsoff(); - iso14443a_setup(FPGA_HF_ISO14443A_SNIFFER); - - // Allocate memory from BigBuf for some buffers - // free all previous allocations first - BigBuf_free(); BigBuf_Clear_ext(false); - clear_trace(); - set_tracing(true); - - // The command (reader -> tag) that we're receiving. - uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedCmdPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - // The response (tag -> reader) that we're receiving. - uint8_t receivedResp[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedRespPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - // allocate the DMA buffer, used to stream samples from the FPGA - uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); - uint8_t *data = dmaBuf; - uint8_t previous_data = 0; - int maxDataLen = 0; - int dataLen = 0; - bool ReaderIsActive = false; - bool TagIsActive = false; - - // We won't start recording the frames that we acquire until we trigger; - // a good trigger condition to get started is probably when we see a - // response from the tag. - // triggered == false -- to wait first for card - //bool triggered = !(param & 0x03); - - - // Set up the demodulator for tag -> reader responses. - DemodInit(receivedResp, receivedRespPar); - - // Set up the demodulator for the reader -> tag commands - UartInit(receivedCmd, receivedCmdPar); - - // Setup and start DMA. - // set transfer address and number of bytes. Start transfer. - if ( !FpgaSetupSscDma(dmaBuf, DMA_BUFFER_SIZE) ){ - if (MF_DBGLEVEL > 1) Dbprintf("[!] FpgaSetupSscDma failed. Exiting"); - return; - } - - tUart* uart = GetUart(); - tDemod* demod = GetDemod(); - - MfSniffInit(); - - uint32_t sniffCounter = 0; - // loop and listen - while (!BUTTON_PRESS()) { - WDT_HIT(); - LED_A_ON(); -/* - if ((sniffCounter & 0x0000FFFF) == 0) { // from time to time - // check if a transaction is completed (timeout after 2000ms). - // if yes, stop the DMA transfer and send what we have so far to the client - if (BigBuf_get_traceLen()) { - MfSniffSend(); - // Reset everything - we missed some sniffed data anyway while the DMA was stopped - sniffCounter = 0; - dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); - data = dmaBuf; - maxDataLen = 0; - ReaderIsActive = false; - TagIsActive = false; - FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // set transfer address and number of bytes. Start transfer. - } - } - */ - - // number of bytes we have processed so far - int register readBufDataP = data - dmaBuf; - // number of bytes already transferred - int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; - if (readBufDataP <= dmaBufDataP) // we are processing the same block of data which is currently being transferred - dataLen = dmaBufDataP - readBufDataP; // number of bytes still to be processed - else - dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP; // number of bytes still to be processed - - // test for length of buffer - if (dataLen > maxDataLen) { // we are more behind than ever... - maxDataLen = dataLen; - if (dataLen > (9 * DMA_BUFFER_SIZE / 10)) { - Dbprintf("[!] blew circular buffer! | datalen %u", dataLen); - break; - } - } - if (dataLen < 1) continue; - - // primary buffer was stopped ( <-- we lost data! - if (!AT91C_BASE_PDC_SSC->PDC_RCR) { - AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t)dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; - Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary - } - // secondary buffer sets as primary, secondary buffer was stopped - if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { - AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf; - AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; - } - - LED_A_OFF(); - - // Need two samples to feed Miller and Manchester-Decoder - if (sniffCounter & 0x01) { - - // no need to try decoding tag data if the reader is sending - if (!TagIsActive) { - uint8_t readerbyte = (previous_data & 0xF0) | (*data >> 4); - if (MillerDecoding(readerbyte, (sniffCounter-1)*4)) { - LogTrace(receivedCmd, uart->len, 0, 0, NULL, true); - DemodReset(); - UartReset(); - } - ReaderIsActive = (uart->state != STATE_UNSYNCD); - } - - // no need to try decoding tag data if the reader is sending - if (!ReaderIsActive) { - uint8_t tagbyte = (previous_data << 4) | (*data & 0x0F); - if (ManchesterDecoding(tagbyte, 0, (sniffCounter-1)*4)) { - LogTrace(receivedResp, demod->len, 0, 0, NULL, false); - DemodReset(); - UartReset(); - } - TagIsActive = (demod->state != DEMOD_UNSYNCD); - } - } - previous_data = *data; - sniffCounter++; - data++; - - if (data == dmaBuf + DMA_BUFFER_SIZE) - data = dmaBuf; - - } // main cycle - - MfSniffEnd(); - switch_off(); -} - -void MfSniffInit(void){ - memset(sniffUID, 0x00, sizeof(sniffUID)); - memset(sniffATQA, 0x00, sizeof(sniffATQA)); - memset(sniffBuf, 0x00, sizeof(sniffBuf)); - sniffSAK = 0; - sniffUIDType = SNF_UID_4; - timerData = 0; -} - -void MfSniffEnd(void){ - LED_B_ON(); - cmd_send(CMD_ACK,0,0,0,0,0); - LED_B_OFF(); -} - -/* -bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { - - // reset on 7-Bit commands from reader - if (reader && (len == 1) && (bitCnt == 7)) { - sniffState = SNF_INIT; - } - - - - switch (sniffState) { - case SNF_INIT:{ - // REQA,WUPA or MAGICWUP from reader - if ((len == 1) && (reader) && (bitCnt == 7) ) { - MfSniffInit(); - sniffState = (data[0] == MIFARE_MAGICWUPC1) ? SNF_MAGIC_WUPC2 : SNF_ATQA; - } - break; - } - case SNF_MAGIC_WUPC2: { - if ((len == 1) && (reader) && (data[0] == MIFARE_MAGICWUPC2) ) { - sniffState = SNF_CARD_IDLE; - } - break; - } - case SNF_ATQA:{ - // ATQA from tag - if ((!reader) && (len == 2)) { - sniffATQA[0] = data[0]; - sniffATQA[1] = data[1]; - sniffState = SNF_UID; - } - break; - } - case SNF_UID: { - - if ( !reader ) break; - if ( len != 9 ) break; - if ( !CheckCrc14443(CRC_14443_A, data, 9)) break; - if ( data[1] != 0x70 ) break; - - Dbprintf("[!] UID | %x", data[0]); - - if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT)) { - // UID_4 - select 4 Byte UID from reader - memcpy(sniffUID, data+2, 4); - sniffUIDType = SNF_UID_4; - sniffState = SNF_SAK; - } else if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2)) { - // UID_7 - Select 2nd part of 7 Byte UID - - // get rid of 0x88 - sniffUID[0] = sniffUID[1]; - sniffUID[1] = sniffUID[2]; - sniffUID[2] = sniffUID[3]; - //new uid bytes - memcpy(sniffUID+3, data+2, 4); - sniffUIDType = SNF_UID_7; - sniffState = SNF_SAK; - } else if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3)) { - // UID_10 - Select 3nd part of 10 Byte UID - // 3+3+4 = 10. - // get ride of previous 0x88 - sniffUID[3] = sniffUID[4]; - sniffUID[4] = sniffUID[5]; - sniffUID[5] = sniffUID[6]; - // new uid bytes - memcpy(sniffUID+6, data+2, 4); - sniffUIDType = SNF_UID_10; - sniffState = SNF_SAK; - } - break; - } - case SNF_SAK:{ - // SAK from card? - if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { - sniffSAK = data[0]; - // CL2 UID part to be expected - if (( sniffSAK == 0x04) && (sniffUIDType == SNF_UID_4)) { - sniffState = SNF_UID; - // CL3 UID part to be expected - } else if ((sniffSAK == 0x04) && (sniffUIDType == SNF_UID_7)) { - sniffState = SNF_UID; - } else { - // select completed - sniffState = SNF_CARD_IDLE; - } - } - break; - } - case SNF_CARD_IDLE:{ // trace the card select sequence - sniffBuf[0] = 0xFF; - sniffBuf[1] = 0xFF; - memcpy(sniffBuf + 2, sniffUID, sizeof(sniffUID)); - memcpy(sniffBuf + 12, sniffATQA, sizeof(sniffATQA)); - sniffBuf[14] = sniffSAK; - sniffBuf[15] = 0xFF; - sniffBuf[16] = 0xFF; - LogTrace(sniffBuf, sizeof(sniffBuf), 0, 0, NULL, true); - sniffState = SNF_CARD_CMD; - } // intentionally no break; - case SNF_CARD_CMD:{ - LogTrace(data, len, 0, 0, NULL, reader); - timerData = GetTickCount(); - break; - } - default: - sniffState = SNF_INIT; - break; - } - return false; -} -*/ - -void RAMFUNC MfSniffSend() { - uint16_t tracelen = BigBuf_get_traceLen(); - uint16_t chunksize = 0; - int packlen = tracelen; // total number of bytes to send - uint8_t *data = BigBuf_get_addr(); - - while (packlen > 0) { - LED_B_ON(); - chunksize = MIN(USB_CMD_DATA_SIZE, packlen); // chunk size 512 - cmd_send(CMD_ACK, 1, tracelen, chunksize, data + tracelen - packlen, chunksize); - packlen -= chunksize; - LED_B_OFF(); - } - - LED_B_ON(); - cmd_send(CMD_ACK, 2, 0, 0, 0, 0); // 2 == data transfer finished. - LED_B_OFF(); +//----------------------------------------------------------------------------- +// Merlok - 2012 +// +// 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. +//----------------------------------------------------------------------------- +// Routines to support mifare classic sniffer. +//----------------------------------------------------------------------------- + +#include "mifaresniff.h" + +//static int sniffState = SNF_INIT; +static uint8_t sniffUIDType = 0; +static uint8_t sniffUID[10] = {0,0,0,0,0,0,0,0,0,0}; +static uint8_t sniffATQA[2] = {0,0}; +static uint8_t sniffSAK = 0; +static uint8_t sniffBuf[17]; +static uint32_t timerData = 0; + +//----------------------------------------------------------------------------- +// MIFARE sniffer. +// +// if no activity for 2sec, it sends the collected data to the client. +//----------------------------------------------------------------------------- +// "hf mf sniff" +void RAMFUNC SniffMifare(uint8_t param) { + // param: + // bit 0 - trigger from first card answer + // bit 1 - trigger from first reader 7-bit request + + // C(red) A(yellow) B(green) + LEDsoff(); + iso14443a_setup(FPGA_HF_ISO14443A_SNIFFER); + + // Allocate memory from BigBuf for some buffers + // free all previous allocations first + BigBuf_free(); BigBuf_Clear_ext(false); + clear_trace(); + set_tracing(true); + + // The command (reader -> tag) that we're receiving. + uint8_t receivedCmd[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedCmdPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // The response (tag -> reader) that we're receiving. + uint8_t receivedResp[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedRespPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // allocate the DMA buffer, used to stream samples from the FPGA + uint8_t *dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + uint8_t *data = dmaBuf; + uint8_t previous_data = 0; + int maxDataLen = 0; + int dataLen = 0; + bool ReaderIsActive = false; + bool TagIsActive = false; + + // We won't start recording the frames that we acquire until we trigger; + // a good trigger condition to get started is probably when we see a + // response from the tag. + // triggered == false -- to wait first for card + //bool triggered = !(param & 0x03); + + + // Set up the demodulator for tag -> reader responses. + DemodInit(receivedResp, receivedRespPar); + + // Set up the demodulator for the reader -> tag commands + UartInit(receivedCmd, receivedCmdPar); + + // Setup and start DMA. + // set transfer address and number of bytes. Start transfer. + if ( !FpgaSetupSscDma(dmaBuf, DMA_BUFFER_SIZE) ){ + if (MF_DBGLEVEL > 1) Dbprintf("[!] FpgaSetupSscDma failed. Exiting"); + return; + } + + tUart* uart = GetUart(); + tDemod* demod = GetDemod(); + + MfSniffInit(); + + uint32_t sniffCounter = 0; + // loop and listen + while (!BUTTON_PRESS()) { + WDT_HIT(); + LED_A_ON(); +/* + if ((sniffCounter & 0x0000FFFF) == 0) { // from time to time + // check if a transaction is completed (timeout after 2000ms). + // if yes, stop the DMA transfer and send what we have so far to the client + if (BigBuf_get_traceLen()) { + MfSniffSend(); + // Reset everything - we missed some sniffed data anyway while the DMA was stopped + sniffCounter = 0; + dmaBuf = BigBuf_malloc(DMA_BUFFER_SIZE); + data = dmaBuf; + maxDataLen = 0; + ReaderIsActive = false; + TagIsActive = false; + FpgaSetupSscDma((uint8_t *)dmaBuf, DMA_BUFFER_SIZE); // set transfer address and number of bytes. Start transfer. + } + } + */ + + // number of bytes we have processed so far + int register readBufDataP = data - dmaBuf; + // number of bytes already transferred + int register dmaBufDataP = DMA_BUFFER_SIZE - AT91C_BASE_PDC_SSC->PDC_RCR; + if (readBufDataP <= dmaBufDataP) // we are processing the same block of data which is currently being transferred + dataLen = dmaBufDataP - readBufDataP; // number of bytes still to be processed + else + dataLen = DMA_BUFFER_SIZE - readBufDataP + dmaBufDataP; // number of bytes still to be processed + + // test for length of buffer + if (dataLen > maxDataLen) { // we are more behind than ever... + maxDataLen = dataLen; + if (dataLen > (9 * DMA_BUFFER_SIZE / 10)) { + Dbprintf("[!] blew circular buffer! | datalen %u", dataLen); + break; + } + } + if (dataLen < 1) continue; + + // primary buffer was stopped ( <-- we lost data! + if (!AT91C_BASE_PDC_SSC->PDC_RCR) { + AT91C_BASE_PDC_SSC->PDC_RPR = (uint32_t)dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RCR = DMA_BUFFER_SIZE; + Dbprintf("[-] RxEmpty ERROR | data length %d", dataLen); // temporary + } + // secondary buffer sets as primary, secondary buffer was stopped + if (!AT91C_BASE_PDC_SSC->PDC_RNCR) { + AT91C_BASE_PDC_SSC->PDC_RNPR = (uint32_t)dmaBuf; + AT91C_BASE_PDC_SSC->PDC_RNCR = DMA_BUFFER_SIZE; + } + + LED_A_OFF(); + + // Need two samples to feed Miller and Manchester-Decoder + if (sniffCounter & 0x01) { + + // no need to try decoding tag data if the reader is sending + if (!TagIsActive) { + uint8_t readerbyte = (previous_data & 0xF0) | (*data >> 4); + if (MillerDecoding(readerbyte, (sniffCounter-1)*4)) { + LogTrace(receivedCmd, uart->len, 0, 0, NULL, true); + DemodReset(); + UartReset(); + } + ReaderIsActive = (uart->state != STATE_UNSYNCD); + } + + // no need to try decoding tag data if the reader is sending + if (!ReaderIsActive) { + uint8_t tagbyte = (previous_data << 4) | (*data & 0x0F); + if (ManchesterDecoding(tagbyte, 0, (sniffCounter-1)*4)) { + LogTrace(receivedResp, demod->len, 0, 0, NULL, false); + DemodReset(); + UartReset(); + } + TagIsActive = (demod->state != DEMOD_UNSYNCD); + } + } + previous_data = *data; + sniffCounter++; + data++; + + if (data == dmaBuf + DMA_BUFFER_SIZE) + data = dmaBuf; + + } // main cycle + + MfSniffEnd(); + switch_off(); +} + +void MfSniffInit(void){ + memset(sniffUID, 0x00, sizeof(sniffUID)); + memset(sniffATQA, 0x00, sizeof(sniffATQA)); + memset(sniffBuf, 0x00, sizeof(sniffBuf)); + sniffSAK = 0; + sniffUIDType = SNF_UID_4; + timerData = 0; +} + +void MfSniffEnd(void){ + LED_B_ON(); + cmd_send(CMD_ACK,0,0,0,0,0); + LED_B_OFF(); +} + +/* +bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader) { + + // reset on 7-Bit commands from reader + if (reader && (len == 1) && (bitCnt == 7)) { + sniffState = SNF_INIT; + } + + + + switch (sniffState) { + case SNF_INIT:{ + // REQA,WUPA or MAGICWUP from reader + if ((len == 1) && (reader) && (bitCnt == 7) ) { + MfSniffInit(); + sniffState = (data[0] == MIFARE_MAGICWUPC1) ? SNF_MAGIC_WUPC2 : SNF_ATQA; + } + break; + } + case SNF_MAGIC_WUPC2: { + if ((len == 1) && (reader) && (data[0] == MIFARE_MAGICWUPC2) ) { + sniffState = SNF_CARD_IDLE; + } + break; + } + case SNF_ATQA:{ + // ATQA from tag + if ((!reader) && (len == 2)) { + sniffATQA[0] = data[0]; + sniffATQA[1] = data[1]; + sniffState = SNF_UID; + } + break; + } + case SNF_UID: { + + if ( !reader ) break; + if ( len != 9 ) break; + if ( !CheckCrc14443(CRC_14443_A, data, 9)) break; + if ( data[1] != 0x70 ) break; + + Dbprintf("[!] UID | %x", data[0]); + + if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT)) { + // UID_4 - select 4 Byte UID from reader + memcpy(sniffUID, data+2, 4); + sniffUIDType = SNF_UID_4; + sniffState = SNF_SAK; + } else if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_2)) { + // UID_7 - Select 2nd part of 7 Byte UID + + // get rid of 0x88 + sniffUID[0] = sniffUID[1]; + sniffUID[1] = sniffUID[2]; + sniffUID[2] = sniffUID[3]; + //new uid bytes + memcpy(sniffUID+3, data+2, 4); + sniffUIDType = SNF_UID_7; + sniffState = SNF_SAK; + } else if ((data[0] == ISO14443A_CMD_ANTICOLL_OR_SELECT_3)) { + // UID_10 - Select 3nd part of 10 Byte UID + // 3+3+4 = 10. + // get ride of previous 0x88 + sniffUID[3] = sniffUID[4]; + sniffUID[4] = sniffUID[5]; + sniffUID[5] = sniffUID[6]; + // new uid bytes + memcpy(sniffUID+6, data+2, 4); + sniffUIDType = SNF_UID_10; + sniffState = SNF_SAK; + } + break; + } + case SNF_SAK:{ + // SAK from card? + if ((!reader) && (len == 3) && (CheckCrc14443(CRC_14443_A, data, 3))) { + sniffSAK = data[0]; + // CL2 UID part to be expected + if (( sniffSAK == 0x04) && (sniffUIDType == SNF_UID_4)) { + sniffState = SNF_UID; + // CL3 UID part to be expected + } else if ((sniffSAK == 0x04) && (sniffUIDType == SNF_UID_7)) { + sniffState = SNF_UID; + } else { + // select completed + sniffState = SNF_CARD_IDLE; + } + } + break; + } + case SNF_CARD_IDLE:{ // trace the card select sequence + sniffBuf[0] = 0xFF; + sniffBuf[1] = 0xFF; + memcpy(sniffBuf + 2, sniffUID, sizeof(sniffUID)); + memcpy(sniffBuf + 12, sniffATQA, sizeof(sniffATQA)); + sniffBuf[14] = sniffSAK; + sniffBuf[15] = 0xFF; + sniffBuf[16] = 0xFF; + LogTrace(sniffBuf, sizeof(sniffBuf), 0, 0, NULL, true); + sniffState = SNF_CARD_CMD; + } // intentionally no break; + case SNF_CARD_CMD:{ + LogTrace(data, len, 0, 0, NULL, reader); + timerData = GetTickCount(); + break; + } + default: + sniffState = SNF_INIT; + break; + } + return false; +} +*/ + +void RAMFUNC MfSniffSend() { + uint16_t tracelen = BigBuf_get_traceLen(); + uint16_t chunksize = 0; + int packlen = tracelen; // total number of bytes to send + uint8_t *data = BigBuf_get_addr(); + + while (packlen > 0) { + LED_B_ON(); + chunksize = MIN(USB_CMD_DATA_SIZE, packlen); // chunk size 512 + cmd_send(CMD_ACK, 1, tracelen, chunksize, data + tracelen - packlen, chunksize); + packlen -= chunksize; + LED_B_OFF(); + } + + LED_B_ON(); + cmd_send(CMD_ACK, 2, 0, 0, 0, 0); // 2 == data transfer finished. + LED_B_OFF(); } \ No newline at end of file diff --git a/armsrc/mifaresniff.h b/armsrc/mifaresniff.h index 537fa2941..efd34fdeb 100644 --- a/armsrc/mifaresniff.h +++ b/armsrc/mifaresniff.h @@ -1,42 +1,42 @@ -//----------------------------------------------------------------------------- -// Merlok - June 2012 -// -// 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. -//----------------------------------------------------------------------------- -// Routines to support mifare classic sniffer. -//----------------------------------------------------------------------------- - -#ifndef __MIFARESNIFF_H -#define __MIFARESNIFF_H - -#include "proxmark3.h" -#include "apps.h" -#include "util.h" -#include "string.h" -#include "iso14443crc.h" -#include "iso14443a.h" -#include "crapto1/crapto1.h" -#include "mifareutil.h" -#include "common.h" - -#define SNF_INIT 0 -#define SNF_NO_FIELD 1 -#define SNF_ATQA 2 -#define SNF_UID 3 -#define SNF_SAK 4 -#define SNF_CARD_IDLE 5 -#define SNF_CARD_CMD 6 -#define SNF_MAGIC_WUPC2 7 - -#define SNF_UID_4 0 -#define SNF_UID_7 0 -#define SNF_UID_10 0 - -void MfSniffInit(void); -bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader); -void RAMFUNC MfSniffSend(void); -void MfSniffEnd(void); - +//----------------------------------------------------------------------------- +// Merlok - June 2012 +// +// 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. +//----------------------------------------------------------------------------- +// Routines to support mifare classic sniffer. +//----------------------------------------------------------------------------- + +#ifndef __MIFARESNIFF_H +#define __MIFARESNIFF_H + +#include "proxmark3.h" +#include "apps.h" +#include "util.h" +#include "string.h" +#include "iso14443crc.h" +#include "iso14443a.h" +#include "crapto1/crapto1.h" +#include "mifareutil.h" +#include "common.h" + +#define SNF_INIT 0 +#define SNF_NO_FIELD 1 +#define SNF_ATQA 2 +#define SNF_UID 3 +#define SNF_SAK 4 +#define SNF_CARD_IDLE 5 +#define SNF_CARD_CMD 6 +#define SNF_MAGIC_WUPC2 7 + +#define SNF_UID_4 0 +#define SNF_UID_7 0 +#define SNF_UID_10 0 + +void MfSniffInit(void); +bool RAMFUNC MfSniffLogic(const uint8_t *data, uint16_t len, uint8_t *parity, uint16_t bitCnt, bool reader); +void RAMFUNC MfSniffSend(void); +void MfSniffEnd(void); + #endif \ No newline at end of file diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 179b9c51e..df85f6cbd 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -1,689 +1,689 @@ -//----------------------------------------------------------------------------- -// Merlok, May 2011, 2012 -// Many authors, whom made it possible -// -// 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. -//----------------------------------------------------------------------------- -// Work with mifare cards. -//----------------------------------------------------------------------------- -#include "mifareutil.h" - -int MF_DBGLEVEL = MF_DBG_ERROR; - -// crypto1 helpers -void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out){ - uint8_t bt = 0; - int i; - - if (len != 1) { - for (i = 0; i < len; i++) - data_out[i] = crypto1_byte(pcs, 0x00, 0) ^ data_in[i]; - } else { - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 0)) << 0; - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 1)) << 1; - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 2)) << 2; - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 3)) << 3; - data_out[0] = bt; - } - return; -} - -void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ - mf_crypto1_decryptEx(pcs, data, len, data); -} - -void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par) { - uint8_t bt = 0; - int i; - par[0] = 0; - - for (i = 0; i < len; i++) { - bt = data[i]; - data[i] = crypto1_byte(pcs, 0x00, 0) ^ data[i]; - if ( ( i & 0x0007 ) == 0) - par[ i >> 3 ] = 0; - par[ i >> 3 ] |= (((filter(pcs->odd) ^ oddparity8(bt)) & 0x01)<<(7-(i&0x0007))); - } -} - -uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data) { - uint8_t bt = 0; - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 0)) << 0; - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 1)) << 1; - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 2)) << 2; - bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 3)) << 3; - return bt; -} - -// send X byte basic commands -int mifare_sendcmd(uint8_t cmd, uint8_t* data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing) { - uint8_t dcmd[data_size+3]; - dcmd[0] = cmd; - memcpy(dcmd+1, data, data_size); - AddCrc14A(dcmd, data_size+1); - ReaderTransmit(dcmd, sizeof(dcmd), timing); - int len = ReaderReceive(answer, answer_parity); - if(!len) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("%02X Cmd failed. Card timeout.", cmd); - len = ReaderReceive(answer,answer_parity); - } - return len; -} - -// send 2 byte commands -int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t *answer, uint8_t *answer_parity, uint32_t *timing) { - uint16_t pos, res; - uint8_t dcmd[4] = {cmd, data, 0x00, 0x00}; - uint8_t ecmd[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t par[1] = {0x00}; // 1 Byte parity is enough here - AddCrc14A(dcmd, 2); - memcpy(ecmd, dcmd, sizeof(dcmd)); - - if (crypted) { - par[0] = 0; - for (pos = 0; pos < 4; pos++) { - ecmd[pos] = crypto1_byte(pcs, 0x00, 0) ^ dcmd[pos]; - par[0] |= (((filter(pcs->odd) ^ oddparity8(dcmd[pos])) & 0x01) << (7-pos)); - } - ReaderTransmitPar(ecmd, sizeof(ecmd), par, timing); - } else { - ReaderTransmit(dcmd, sizeof(dcmd), timing); - } - - int len = ReaderReceive(answer, par); - - if (answer_parity) *answer_parity = par[0]; - - if (crypted == CRYPT_ALL) { - if (len == 1) { - res = 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 0)) << 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 1)) << 1; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 2)) << 2; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 3)) << 3; - answer[0] = res; - } else { - for (pos = 0; pos < len; pos++) - answer[pos] = crypto1_byte(pcs, 0x00, 0) ^ answer[pos]; - } - } - return len; -} - -// mifare classic commands -int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested) { - return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL); -} - -int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) { - int len; - uint32_t pos, nt, ntpp; // Supplied tag nonce - uint8_t par[1] = {0x00}; - uint8_t nr[4]; - uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - // "random" reader nonce: - num_to_bytes( prng_successor( GetTickCount(), 32), 4, nr); - - // Transmit MIFARE_CLASSIC_AUTH - len = mifare_sendcmd_short(pcs, isNested, 0x60 + (keyType & 0x01), blockNo, receivedAnswer, receivedAnswerPar, timing); - if (len != 4) return 1; - - // Save the tag nonce (nt) - nt = bytes_to_num(receivedAnswer, 4); - - // ----------------------------- crypto1 create - if (isNested) - crypto1_destroy(pcs); - - // Init cipher with key - crypto1_create(pcs, ui64Key); - - if (isNested == AUTH_NESTED) { - // decrypt nt with help of new key - nt = crypto1_word(pcs, nt ^ uid, 1) ^ nt; - } else { - // Load (plain) uid^nt into the cipher - crypto1_word(pcs, nt ^ uid, 0); - } - - // some statistic - if (!ntptr && (MF_DBGLEVEL >= MF_DBG_EXTENDED)) - Dbprintf("auth uid: %08x | nr: %08x | nt: %08x", uid, nr, nt); - - // save Nt - if (ntptr) - *ntptr = nt; - - // Generate (encrypted) nr+parity by loading it into the cipher (Nr) - par[0] = 0; - for (pos = 0; pos < 4; pos++) { - mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos]; - par[0] |= (((filter(pcs->odd) ^ oddparity8(nr[pos])) & 0x01) << (7-pos)); - } - - // Skip 32 bits in pseudo random generator - nt = prng_successor(nt, 32); - - // ar+parity - for (pos = 4; pos < 8; pos++) { - nt = prng_successor(nt,8); - mf_nr_ar[pos] = crypto1_byte(pcs,0x00,0) ^ (nt & 0xff); - par[0] |= (((filter(pcs->odd) ^ oddparity8(nt & 0xff)) & 0x01) << (7-pos)); - } - - // Transmit reader nonce and reader answer - ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); - - // Receive 4 byte tag answer - len = ReaderReceive(receivedAnswer, receivedAnswerPar); - if (!len) { - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication failed. Card timeout."); - return 2; - } - - ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0,0); - - if (ntpp != bytes_to_num(receivedAnswer, 4)) { - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication failed. Error card response."); - return 3; - } - return 0; -} - -int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { - - int len; - uint8_t bt[2] = {0x00, 0x00}; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); - if (len == 1) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; - } - if (len != 18) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: wrong response len: %x (expected 18)", len); - return 2; - } - - memcpy(bt, receivedAnswer + 16, 2); - AddCrc14A(receivedAnswer, 16); - if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Cmd CRC response error."); - return 3; - } - - memcpy(blockData, receivedAnswer, 16); - return 0; -} - -// mifare ultralight commands -int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack){ - - uint16_t len = 0; - uint8_t resp[4] = {0x00, 0x00, 0x00, 0x00}; - uint8_t respPar[1] = {0x00}; - uint8_t key[4] = {0x00, 0x00, 0x00, 0x00}; - memcpy(key, keybytes, 4); - - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) - Dbprintf("EV1 Auth : %02x%02x%02x%02x", key[0], key[1], key[2], key[3]); - - len = mifare_sendcmd(MIFARE_ULEV1_AUTH, key, sizeof(key), resp, respPar, NULL); - - if (len != 4) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x %u", resp[0], len); - return 0; - } - - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) - Dbprintf("Auth Resp: %02x%02x%02x%02x", resp[0],resp[1],resp[2],resp[3]); - - memcpy(pack, resp, 4); - return 1; -} - -int mifare_ultra_auth(uint8_t *keybytes){ - - /// 3des2k - uint8_t random_a[8] = {1,1,1,1,1,1,1,1}; - uint8_t random_b[8] = {0x00}; - uint8_t enc_random_b[8] = {0x00}; - uint8_t rnd_ab[16] = {0x00}; - uint8_t IV[8] = {0x00}; - uint8_t key[16] = {0x00}; - memcpy(key, keybytes, 16); - - uint16_t len = 0; - uint8_t resp[19] = {0x00}; - uint8_t respPar[3] = {0,0,0}; - - // REQUEST AUTHENTICATION - len = mifare_sendcmd_short(NULL, 1, MIFARE_ULC_AUTH_1, 0x00, resp, respPar ,NULL); - if (len != 11) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]); - return 0; - } - - // tag nonce. - memcpy(enc_random_b,resp+1,8); - - // decrypt nonce. - tdes_2key_dec((void*)random_b, (void*)enc_random_b, sizeof(random_b), (const void*)key, IV ); - rol(random_b,8); - memcpy(rnd_ab ,random_a,8); - memcpy(rnd_ab+8,random_b,8); - - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { - Dbprintf("enc_B: %02x %02x %02x %02x %02x %02x %02x %02x", - enc_random_b[0],enc_random_b[1],enc_random_b[2],enc_random_b[3],enc_random_b[4],enc_random_b[5],enc_random_b[6],enc_random_b[7]); - - Dbprintf(" B: %02x %02x %02x %02x %02x %02x %02x %02x", - random_b[0],random_b[1],random_b[2],random_b[3],random_b[4],random_b[5],random_b[6],random_b[7]); - - Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x", - rnd_ab[0],rnd_ab[1],rnd_ab[2],rnd_ab[3],rnd_ab[4],rnd_ab[5],rnd_ab[6],rnd_ab[7]); - - Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x", - rnd_ab[8],rnd_ab[9],rnd_ab[10],rnd_ab[11],rnd_ab[12],rnd_ab[13],rnd_ab[14],rnd_ab[15] ); - } - - // encrypt out, in, length, key, iv - tdes_2key_enc(rnd_ab, rnd_ab, sizeof(rnd_ab), key, enc_random_b); - - len = mifare_sendcmd(MIFARE_ULC_AUTH_2, rnd_ab, sizeof(rnd_ab), resp, respPar, NULL); - if (len != 11) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]); - return 0; - } - - uint8_t enc_resp[8] = { 0,0,0,0,0,0,0,0 }; - uint8_t resp_random_a[8] = { 0,0,0,0,0,0,0,0 }; - memcpy(enc_resp, resp+1, 8); - - // decrypt out, in, length, key, iv - tdes_2key_dec(resp_random_a, enc_resp, 8, key, enc_random_b); - if ( memcmp(resp_random_a, random_a, 8) != 0 ) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("failed authentication"); - return 0; - } - - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { - Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x", - rnd_ab[0],rnd_ab[1],rnd_ab[2],rnd_ab[3], - rnd_ab[4],rnd_ab[5],rnd_ab[6],rnd_ab[7]); - - Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x", - rnd_ab[8],rnd_ab[9],rnd_ab[10],rnd_ab[11], - rnd_ab[12],rnd_ab[13],rnd_ab[14],rnd_ab[15]); - - Dbprintf("a: %02x %02x %02x %02x %02x %02x %02x %02x", - random_a[0],random_a[1],random_a[2],random_a[3], - random_a[4],random_a[5],random_a[6],random_a[7]); - - Dbprintf("b: %02x %02x %02x %02x %02x %02x %02x %02x", - resp_random_a[0],resp_random_a[1],resp_random_a[2],resp_random_a[3], - resp_random_a[4],resp_random_a[5],resp_random_a[6],resp_random_a[7]); - } - return 1; -} - -int mifare_ultra_readblockEx(uint8_t blockNo, uint8_t *blockData) { - uint16_t len = 0; - uint8_t bt[2] = {0x00, 0x00}; - uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; - - len = mifare_sendcmd_short(NULL, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); - if (len == 1) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; - } - if (len != 18) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); - return 2; - } - - memcpy(bt, receivedAnswer + 16, 2); - AddCrc14A(receivedAnswer, 16); - if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd CRC response error."); - return 3; - } - - memcpy(blockData, receivedAnswer, 14); - return 0; -} -int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { - #define MFU_MAX_RETRIES 5 - uint8_t res; - - for (uint8_t retries = 0; retries < MFU_MAX_RETRIES; ++retries) { - res = mifare_ultra_readblockEx(blockNo, blockData); - - // break if OK, or NACK. - switch ( res ) { - case 0: - case 1: - return res; - default: - continue; - } - } - return res; -} - -int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { - // variables - uint16_t len = 0; - uint32_t pos = 0; - uint8_t par[3] = {0x00, 0x00, 0x00}; // enough for 18 Bytes to send - byte_t res = 0; - - uint8_t d_block[18], d_block_enc[18]; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - // command MIFARE_CLASSIC_WRITEBLOCK - len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); - - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; - } - - memcpy(d_block, blockData, 16); - AddCrc14A(d_block, 16); - - // crypto - for (pos = 0; pos < 18; pos++) { - d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; - par[pos>>3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos&0x0007))); - } - - ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); - - // Receive the response - len = ReaderReceive(receivedAnswer, receivedAnswerPar); - - res = 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; - res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; - - if ((len != 1) || (res != 0x0A)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); - return 2; - } - return 0; -} - -/* // command not needed, but left for future testing -int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { - uint16_t len; - uint8_t par[3] = {0}; // enough for 18 parity bits - uint8_t d_block[18] = {0x00}; - uint8_t receivedAnswer[MAX_FRAME_SIZE]; - uint8_t receivedAnswerPar[MAX_PARITY_SIZE]; - - len = mifare_sendcmd_short(NULL, true, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); - - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (MF_DBGLEVEL >= MF_DBG_ERROR) - Dbprintf("Cmd Addr Error: %02x", receivedAnswer[0]); - return 1; - } - - memcpy(d_block, blockData, 16); - AddCrc14A(d_block, 16); - - ReaderTransmitPar(d_block, sizeof(d_block), par, NULL); - - len = ReaderReceive(receivedAnswer, receivedAnswerPar); - - if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK - if (MF_DBGLEVEL >= MF_DBG_ERROR) - Dbprintf("Cmd Data Error: %02x %d", receivedAnswer[0],len); - return 2; - } - return 0; -} -*/ - -int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) { - uint16_t len = 0; - uint8_t block[5] = {blockNo, 0x00, 0x00, 0x00, 0x00 }; - uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; - - // command MIFARE_CLASSIC_WRITEBLOCK - memcpy(block+1, blockData, 4); - - len = mifare_sendcmd( MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, receivedAnswerPar, NULL); - - if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK - if (MF_DBGLEVEL >= MF_DBG_ERROR) - Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0],len); - return 1; - } - return 0; -} -int mifare_classic_halt_ex(struct Crypto1State *pcs) { - uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; - uint16_t len = mifare_sendcmd_short(pcs, (pcs == NULL) ? CRYPT_NONE : CRYPT_ALL, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); - if (len != 0) { - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("halt warning. response len: %x", len); - return 1; - } - return 0; -} -int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) { - return mifare_classic_halt_ex(pcs); -} - -int mifare_ultra_halt() { - uint16_t len = 0; - uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; - len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); - if (len != 0) { - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("halt warning. response len: %x", len); - return 1; - } - return 0; -} - - -// Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), -// plus evtl. 8 sectors with 16 blocks each (4k cards) -uint8_t NumBlocksPerSector(uint8_t sectorNo) { - return (sectorNo < 32) ? 4 : 16; -} - -uint8_t FirstBlockOfSector(uint8_t sectorNo) { - if (sectorNo < 32) - return sectorNo * 4; - else - return 32*4 + (sectorNo - 32) * 16; - -} - -// work with emulator memory -void emlSetMem(uint8_t *data, int blockNum, int blocksCount) { - emlSetMem_xt(data, blockNum, blocksCount, 16); -} - -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { - uint8_t* emCARD = BigBuf_get_EM_addr(); - memcpy(emCARD + blockNum * blockBtWidth, data, blocksCount * blockBtWidth); -} - -void emlGetMem(uint8_t *data, int blockNum, int blocksCount) { - uint8_t* emCARD = BigBuf_get_EM_addr(); - memcpy(data, emCARD + blockNum * 16, blocksCount * 16); -} - -void emlGetMemBt(uint8_t *data, int bytePtr, int byteCount) { - uint8_t* emCARD = BigBuf_get_EM_addr(); - memcpy(data, emCARD + bytePtr, byteCount); -} - -int emlCheckValBl(int blockNum) { - uint8_t* emCARD = BigBuf_get_EM_addr(); - uint8_t* data = emCARD + blockNum * 16; - - if ((data[0] != (data[4] ^ 0xff)) || (data[0] != data[8]) || - (data[1] != (data[5] ^ 0xff)) || (data[1] != data[9]) || - (data[2] != (data[6] ^ 0xff)) || (data[2] != data[10]) || - (data[3] != (data[7] ^ 0xff)) || (data[3] != data[11]) || - (data[12] != (data[13] ^ 0xff)) || (data[12] != data[14]) || - (data[12] != (data[15] ^ 0xff)) - ) - return 1; - return 0; -} - -int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum) { - uint8_t* emCARD = BigBuf_get_EM_addr(); - uint8_t* data = emCARD + blockNum * 16; - - if (emlCheckValBl(blockNum)) - return 1; - - memcpy(blReg, data, 4); - *blBlock = data[12]; - return 0; -} - -int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum) { - uint8_t* emCARD = BigBuf_get_EM_addr(); - uint8_t* data = emCARD + blockNum * 16; - - memcpy(data + 0, &blReg, 4); - memcpy(data + 8, &blReg, 4); - blReg = blReg ^ 0xffffffff; - memcpy(data + 4, &blReg, 4); - - data[12] = blBlock; - data[13] = blBlock ^ 0xff; - data[14] = blBlock; - data[15] = blBlock ^ 0xff; - - return 0; -} - -uint64_t emlGetKey(int sectorNum, int keyType) { - uint8_t key[6] = {0x00}; - uint8_t* emCARD = BigBuf_get_EM_addr(); - memcpy(key, emCARD + 16 * (FirstBlockOfSector(sectorNum) + NumBlocksPerSector(sectorNum) - 1) + keyType * 10, 6); - return bytes_to_num(key, 6); -} - -void emlClearMem(void) { - const uint8_t trailer[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; - const uint8_t uid[] = {0xe6, 0x84, 0x87, 0xf3, 0x16, 0x88, 0x04, 0x00, 0x46, 0x8e, 0x45, 0x55, 0x4d, 0x70, 0x41, 0x04}; - uint8_t* emCARD = BigBuf_get_EM_addr(); - memset(emCARD, 0, CARD_MEMORY_SIZE); - - // fill sectors trailer data - for(uint16_t b = 3; b < 256; ((b < 127) ? (b += 4) : (b += 16))) - emlSetMem((uint8_t *)trailer, b, 1); - - // uid - emlSetMem((uint8_t *)uid, 0, 1); - return; -} - - -// Mifare desfire commands -int mifare_sendcmd_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing) { - uint8_t dcmd[5] = {cmd, data[0], data[1], 0x00, 0x00}; - AddCrc14A(dcmd, 3); - - ReaderTransmit(dcmd, sizeof(dcmd), NULL); - int len = ReaderReceive(answer, answer_parity); - if(!len) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed. Card timeout."); - return 1; - } - return len; -} - -int mifare_sendcmd_special2(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer,uint8_t *answer_parity, uint32_t *timing) { - uint8_t dcmd[20] = {0x00}; - dcmd[0] = cmd; - memcpy(dcmd+1,data,17); - AddCrc14A(dcmd, 18); - - ReaderTransmit(dcmd, sizeof(dcmd), NULL); - int len = ReaderReceive(answer, answer_parity); - if(!len){ - if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed. Card timeout."); - return 1; - } - return len; -} - -int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData){ - - int len; - // load key, keynumber - uint8_t data[2]={MFDES_AUTHENTICATE, 0x00}; - uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; - - len = mifare_sendcmd_special(NULL, 1, 0x02, data, receivedAnswer,receivedAnswerPar,NULL); - if (len == 1) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) - Dbprintf("Cmd Error: %02x", receivedAnswer[0]); - return 1; - } - - if (len == 12) { - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { - Dbprintf("Auth1 Resp: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - receivedAnswer[0],receivedAnswer[1],receivedAnswer[2],receivedAnswer[3],receivedAnswer[4], - receivedAnswer[5],receivedAnswer[6],receivedAnswer[7],receivedAnswer[8],receivedAnswer[9], - receivedAnswer[10],receivedAnswer[11]); - } - memcpy(blockData, receivedAnswer, 12); - return 0; - } - return 1; -} - -int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData){ - - int len; - uint8_t data[17] = {MFDES_AUTHENTICATION_FRAME}; - memcpy(data+1,key,16); - - uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; - uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; - - len = mifare_sendcmd_special2(NULL, 1, 0x03, data, receivedAnswer, receivedAnswerPar ,NULL); - - if ((receivedAnswer[0] == 0x03) && (receivedAnswer[1] == 0xae)) { - if (MF_DBGLEVEL >= MF_DBG_ERROR) - Dbprintf("Auth Error: %02x %02x", receivedAnswer[0], receivedAnswer[1]); - return 1; - } - - if (len == 12){ - if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { - Dbprintf("Auth2 Resp: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - receivedAnswer[0],receivedAnswer[1],receivedAnswer[2],receivedAnswer[3],receivedAnswer[4], - receivedAnswer[5],receivedAnswer[6],receivedAnswer[7],receivedAnswer[8],receivedAnswer[9], - receivedAnswer[10],receivedAnswer[11]); - } - memcpy(blockData, receivedAnswer, 12); - return 0; - } - return 1; -} +//----------------------------------------------------------------------------- +// Merlok, May 2011, 2012 +// Many authors, whom made it possible +// +// 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. +//----------------------------------------------------------------------------- +// Work with mifare cards. +//----------------------------------------------------------------------------- +#include "mifareutil.h" + +int MF_DBGLEVEL = MF_DBG_ERROR; + +// crypto1 helpers +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out){ + uint8_t bt = 0; + int i; + + if (len != 1) { + for (i = 0; i < len; i++) + data_out[i] = crypto1_byte(pcs, 0x00, 0) ^ data_in[i]; + } else { + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 0)) << 0; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 1)) << 1; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 2)) << 2; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data_in[0], 3)) << 3; + data_out[0] = bt; + } + return; +} + +void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *data, int len){ + mf_crypto1_decryptEx(pcs, data, len, data); +} + +void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par) { + uint8_t bt = 0; + int i; + par[0] = 0; + + for (i = 0; i < len; i++) { + bt = data[i]; + data[i] = crypto1_byte(pcs, 0x00, 0) ^ data[i]; + if ( ( i & 0x0007 ) == 0) + par[ i >> 3 ] = 0; + par[ i >> 3 ] |= (((filter(pcs->odd) ^ oddparity8(bt)) & 0x01)<<(7-(i&0x0007))); + } +} + +uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data) { + uint8_t bt = 0; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 0)) << 0; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 1)) << 1; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 2)) << 2; + bt |= (crypto1_bit(pcs, 0, 0) ^ BIT(data, 3)) << 3; + return bt; +} + +// send X byte basic commands +int mifare_sendcmd(uint8_t cmd, uint8_t* data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing) { + uint8_t dcmd[data_size+3]; + dcmd[0] = cmd; + memcpy(dcmd+1, data, data_size); + AddCrc14A(dcmd, data_size+1); + ReaderTransmit(dcmd, sizeof(dcmd), timing); + int len = ReaderReceive(answer, answer_parity); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("%02X Cmd failed. Card timeout.", cmd); + len = ReaderReceive(answer,answer_parity); + } + return len; +} + +// send 2 byte commands +int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t *answer, uint8_t *answer_parity, uint32_t *timing) { + uint16_t pos, res; + uint8_t dcmd[4] = {cmd, data, 0x00, 0x00}; + uint8_t ecmd[4] = {0x00, 0x00, 0x00, 0x00}; + uint8_t par[1] = {0x00}; // 1 Byte parity is enough here + AddCrc14A(dcmd, 2); + memcpy(ecmd, dcmd, sizeof(dcmd)); + + if (crypted) { + par[0] = 0; + for (pos = 0; pos < 4; pos++) { + ecmd[pos] = crypto1_byte(pcs, 0x00, 0) ^ dcmd[pos]; + par[0] |= (((filter(pcs->odd) ^ oddparity8(dcmd[pos])) & 0x01) << (7-pos)); + } + ReaderTransmitPar(ecmd, sizeof(ecmd), par, timing); + } else { + ReaderTransmit(dcmd, sizeof(dcmd), timing); + } + + int len = ReaderReceive(answer, par); + + if (answer_parity) *answer_parity = par[0]; + + if (crypted == CRYPT_ALL) { + if (len == 1) { + res = 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(answer[0], 3)) << 3; + answer[0] = res; + } else { + for (pos = 0; pos < len; pos++) + answer[pos] = crypto1_byte(pcs, 0x00, 0) ^ answer[pos]; + } + } + return len; +} + +// mifare classic commands +int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested) { + return mifare_classic_authex(pcs, uid, blockNo, keyType, ui64Key, isNested, NULL, NULL); +} + +int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t *ntptr, uint32_t *timing) { + int len; + uint32_t pos, nt, ntpp; // Supplied tag nonce + uint8_t par[1] = {0x00}; + uint8_t nr[4]; + uint8_t mf_nr_ar[] = { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 }; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // "random" reader nonce: + num_to_bytes( prng_successor( GetTickCount(), 32), 4, nr); + + // Transmit MIFARE_CLASSIC_AUTH + len = mifare_sendcmd_short(pcs, isNested, 0x60 + (keyType & 0x01), blockNo, receivedAnswer, receivedAnswerPar, timing); + if (len != 4) return 1; + + // Save the tag nonce (nt) + nt = bytes_to_num(receivedAnswer, 4); + + // ----------------------------- crypto1 create + if (isNested) + crypto1_destroy(pcs); + + // Init cipher with key + crypto1_create(pcs, ui64Key); + + if (isNested == AUTH_NESTED) { + // decrypt nt with help of new key + nt = crypto1_word(pcs, nt ^ uid, 1) ^ nt; + } else { + // Load (plain) uid^nt into the cipher + crypto1_word(pcs, nt ^ uid, 0); + } + + // some statistic + if (!ntptr && (MF_DBGLEVEL >= MF_DBG_EXTENDED)) + Dbprintf("auth uid: %08x | nr: %08x | nt: %08x", uid, nr, nt); + + // save Nt + if (ntptr) + *ntptr = nt; + + // Generate (encrypted) nr+parity by loading it into the cipher (Nr) + par[0] = 0; + for (pos = 0; pos < 4; pos++) { + mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos]; + par[0] |= (((filter(pcs->odd) ^ oddparity8(nr[pos])) & 0x01) << (7-pos)); + } + + // Skip 32 bits in pseudo random generator + nt = prng_successor(nt, 32); + + // ar+parity + for (pos = 4; pos < 8; pos++) { + nt = prng_successor(nt,8); + mf_nr_ar[pos] = crypto1_byte(pcs,0x00,0) ^ (nt & 0xff); + par[0] |= (((filter(pcs->odd) ^ oddparity8(nt & 0xff)) & 0x01) << (7-pos)); + } + + // Transmit reader nonce and reader answer + ReaderTransmitPar(mf_nr_ar, sizeof(mf_nr_ar), par, NULL); + + // Receive 4 byte tag answer + len = ReaderReceive(receivedAnswer, receivedAnswerPar); + if (!len) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication failed. Card timeout."); + return 2; + } + + ntpp = prng_successor(nt, 32) ^ crypto1_word(pcs, 0,0); + + if (ntpp != bytes_to_num(receivedAnswer, 4)) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("Authentication failed. Error card response."); + return 3; + } + return 0; +} + +int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { + + int len; + uint8_t bt[2] = {0x00, 0x00}; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + if (len == 1) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return 1; + } + if (len != 18) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: wrong response len: %x (expected 18)", len); + return 2; + } + + memcpy(bt, receivedAnswer + 16, 2); + AddCrc14A(receivedAnswer, 16); + if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { + if (MF_DBGLEVEL >= MF_DBG_ALL) Dbprintf("Cmd CRC response error."); + return 3; + } + + memcpy(blockData, receivedAnswer, 16); + return 0; +} + +// mifare ultralight commands +int mifare_ul_ev1_auth(uint8_t *keybytes, uint8_t *pack){ + + uint16_t len = 0; + uint8_t resp[4] = {0x00, 0x00, 0x00, 0x00}; + uint8_t respPar[1] = {0x00}; + uint8_t key[4] = {0x00, 0x00, 0x00, 0x00}; + memcpy(key, keybytes, 4); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) + Dbprintf("EV1 Auth : %02x%02x%02x%02x", key[0], key[1], key[2], key[3]); + + len = mifare_sendcmd(MIFARE_ULEV1_AUTH, key, sizeof(key), resp, respPar, NULL); + + if (len != 4) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x %u", resp[0], len); + return 0; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) + Dbprintf("Auth Resp: %02x%02x%02x%02x", resp[0],resp[1],resp[2],resp[3]); + + memcpy(pack, resp, 4); + return 1; +} + +int mifare_ultra_auth(uint8_t *keybytes){ + + /// 3des2k + uint8_t random_a[8] = {1,1,1,1,1,1,1,1}; + uint8_t random_b[8] = {0x00}; + uint8_t enc_random_b[8] = {0x00}; + uint8_t rnd_ab[16] = {0x00}; + uint8_t IV[8] = {0x00}; + uint8_t key[16] = {0x00}; + memcpy(key, keybytes, 16); + + uint16_t len = 0; + uint8_t resp[19] = {0x00}; + uint8_t respPar[3] = {0,0,0}; + + // REQUEST AUTHENTICATION + len = mifare_sendcmd_short(NULL, 1, MIFARE_ULC_AUTH_1, 0x00, resp, respPar ,NULL); + if (len != 11) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]); + return 0; + } + + // tag nonce. + memcpy(enc_random_b,resp+1,8); + + // decrypt nonce. + tdes_2key_dec((void*)random_b, (void*)enc_random_b, sizeof(random_b), (const void*)key, IV ); + rol(random_b,8); + memcpy(rnd_ab ,random_a,8); + memcpy(rnd_ab+8,random_b,8); + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("enc_B: %02x %02x %02x %02x %02x %02x %02x %02x", + enc_random_b[0],enc_random_b[1],enc_random_b[2],enc_random_b[3],enc_random_b[4],enc_random_b[5],enc_random_b[6],enc_random_b[7]); + + Dbprintf(" B: %02x %02x %02x %02x %02x %02x %02x %02x", + random_b[0],random_b[1],random_b[2],random_b[3],random_b[4],random_b[5],random_b[6],random_b[7]); + + Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[0],rnd_ab[1],rnd_ab[2],rnd_ab[3],rnd_ab[4],rnd_ab[5],rnd_ab[6],rnd_ab[7]); + + Dbprintf("rnd_ab: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[8],rnd_ab[9],rnd_ab[10],rnd_ab[11],rnd_ab[12],rnd_ab[13],rnd_ab[14],rnd_ab[15] ); + } + + // encrypt out, in, length, key, iv + tdes_2key_enc(rnd_ab, rnd_ab, sizeof(rnd_ab), key, enc_random_b); + + len = mifare_sendcmd(MIFARE_ULC_AUTH_2, rnd_ab, sizeof(rnd_ab), resp, respPar, NULL); + if (len != 11) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", resp[0]); + return 0; + } + + uint8_t enc_resp[8] = { 0,0,0,0,0,0,0,0 }; + uint8_t resp_random_a[8] = { 0,0,0,0,0,0,0,0 }; + memcpy(enc_resp, resp+1, 8); + + // decrypt out, in, length, key, iv + tdes_2key_dec(resp_random_a, enc_resp, 8, key, enc_random_b); + if ( memcmp(resp_random_a, random_a, 8) != 0 ) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("failed authentication"); + return 0; + } + + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[0],rnd_ab[1],rnd_ab[2],rnd_ab[3], + rnd_ab[4],rnd_ab[5],rnd_ab[6],rnd_ab[7]); + + Dbprintf("e_AB: %02x %02x %02x %02x %02x %02x %02x %02x", + rnd_ab[8],rnd_ab[9],rnd_ab[10],rnd_ab[11], + rnd_ab[12],rnd_ab[13],rnd_ab[14],rnd_ab[15]); + + Dbprintf("a: %02x %02x %02x %02x %02x %02x %02x %02x", + random_a[0],random_a[1],random_a[2],random_a[3], + random_a[4],random_a[5],random_a[6],random_a[7]); + + Dbprintf("b: %02x %02x %02x %02x %02x %02x %02x %02x", + resp_random_a[0],resp_random_a[1],resp_random_a[2],resp_random_a[3], + resp_random_a[4],resp_random_a[5],resp_random_a[6],resp_random_a[7]); + } + return 1; +} + +int mifare_ultra_readblockEx(uint8_t blockNo, uint8_t *blockData) { + uint16_t len = 0; + uint8_t bt[2] = {0x00, 0x00}; + uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; + + len = mifare_sendcmd_short(NULL, 1, ISO14443A_CMD_READBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + if (len == 1) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return 1; + } + if (len != 18) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: card timeout. len: %x", len); + return 2; + } + + memcpy(bt, receivedAnswer + 16, 2); + AddCrc14A(receivedAnswer, 16); + if (bt[0] != receivedAnswer[16] || bt[1] != receivedAnswer[17]) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd CRC response error."); + return 3; + } + + memcpy(blockData, receivedAnswer, 14); + return 0; +} +int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData) { + #define MFU_MAX_RETRIES 5 + uint8_t res; + + for (uint8_t retries = 0; retries < MFU_MAX_RETRIES; ++retries) { + res = mifare_ultra_readblockEx(blockNo, blockData); + + // break if OK, or NACK. + switch ( res ) { + case 0: + case 1: + return res; + default: + continue; + } + } + return res; +} + +int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData) { + // variables + uint16_t len = 0; + uint32_t pos = 0; + uint8_t par[3] = {0x00, 0x00, 0x00}; // enough for 18 Bytes to send + byte_t res = 0; + + uint8_t d_block[18], d_block_enc[18]; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // command MIFARE_CLASSIC_WRITEBLOCK + len = mifare_sendcmd_short(pcs, 1, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return 1; + } + + memcpy(d_block, blockData, 16); + AddCrc14A(d_block, 16); + + // crypto + for (pos = 0; pos < 18; pos++) { + d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; + par[pos>>3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos&0x0007))); + } + + ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); + + // Receive the response + len = ReaderReceive(receivedAnswer, receivedAnswerPar); + + res = 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 0)) << 0; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 1)) << 1; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 2)) << 2; + res |= (crypto1_bit(pcs, 0, 0) ^ BIT(receivedAnswer[0], 3)) << 3; + + if ((len != 1) || (res != 0x0A)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Cmd send data2 Error: %02x", res); + return 2; + } + return 0; +} + +/* // command not needed, but left for future testing +int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData) { + uint16_t len; + uint8_t par[3] = {0}; // enough for 18 parity bits + uint8_t d_block[18] = {0x00}; + uint8_t receivedAnswer[MAX_FRAME_SIZE]; + uint8_t receivedAnswerPar[MAX_PARITY_SIZE]; + + len = mifare_sendcmd_short(NULL, true, ISO14443A_CMD_WRITEBLOCK, blockNo, receivedAnswer, receivedAnswerPar, NULL); + + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Addr Error: %02x", receivedAnswer[0]); + return 1; + } + + memcpy(d_block, blockData, 16); + AddCrc14A(d_block, 16); + + ReaderTransmitPar(d_block, sizeof(d_block), par, NULL); + + len = ReaderReceive(receivedAnswer, receivedAnswerPar); + + if ((len != 1) || (receivedAnswer[0] != 0x0A)) { // 0x0a - ACK + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Data Error: %02x %d", receivedAnswer[0],len); + return 2; + } + return 0; +} +*/ + +int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData) { + uint16_t len = 0; + uint8_t block[5] = {blockNo, 0x00, 0x00, 0x00, 0x00 }; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_MIFARE_PARITY_SIZE] = {0x00}; + + // command MIFARE_CLASSIC_WRITEBLOCK + memcpy(block+1, blockData, 4); + + len = mifare_sendcmd( MIFARE_ULC_WRITE, block, sizeof(block), receivedAnswer, receivedAnswerPar, NULL); + + if (receivedAnswer[0] != 0x0A) { // 0x0a - ACK + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Send Error: %02x %d", receivedAnswer[0],len); + return 1; + } + return 0; +} +int mifare_classic_halt_ex(struct Crypto1State *pcs) { + uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; + uint16_t len = mifare_sendcmd_short(pcs, (pcs == NULL) ? CRYPT_NONE : CRYPT_ALL, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); + if (len != 0) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("halt warning. response len: %x", len); + return 1; + } + return 0; +} +int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid) { + return mifare_classic_halt_ex(pcs); +} + +int mifare_ultra_halt() { + uint16_t len = 0; + uint8_t receivedAnswer[4] = {0x00, 0x00, 0x00, 0x00}; + len = mifare_sendcmd_short(NULL, CRYPT_NONE, ISO14443A_CMD_HALT, 0x00, receivedAnswer, NULL, NULL); + if (len != 0) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) Dbprintf("halt warning. response len: %x", len); + return 1; + } + return 0; +} + + +// Mifare Memory Structure: up to 32 Sectors with 4 blocks each (1k and 2k cards), +// plus evtl. 8 sectors with 16 blocks each (4k cards) +uint8_t NumBlocksPerSector(uint8_t sectorNo) { + return (sectorNo < 32) ? 4 : 16; +} + +uint8_t FirstBlockOfSector(uint8_t sectorNo) { + if (sectorNo < 32) + return sectorNo * 4; + else + return 32*4 + (sectorNo - 32) * 16; + +} + +// work with emulator memory +void emlSetMem(uint8_t *data, int blockNum, int blocksCount) { + emlSetMem_xt(data, blockNum, blocksCount, 16); +} + +void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth) { + uint8_t* emCARD = BigBuf_get_EM_addr(); + memcpy(emCARD + blockNum * blockBtWidth, data, blocksCount * blockBtWidth); +} + +void emlGetMem(uint8_t *data, int blockNum, int blocksCount) { + uint8_t* emCARD = BigBuf_get_EM_addr(); + memcpy(data, emCARD + blockNum * 16, blocksCount * 16); +} + +void emlGetMemBt(uint8_t *data, int bytePtr, int byteCount) { + uint8_t* emCARD = BigBuf_get_EM_addr(); + memcpy(data, emCARD + bytePtr, byteCount); +} + +int emlCheckValBl(int blockNum) { + uint8_t* emCARD = BigBuf_get_EM_addr(); + uint8_t* data = emCARD + blockNum * 16; + + if ((data[0] != (data[4] ^ 0xff)) || (data[0] != data[8]) || + (data[1] != (data[5] ^ 0xff)) || (data[1] != data[9]) || + (data[2] != (data[6] ^ 0xff)) || (data[2] != data[10]) || + (data[3] != (data[7] ^ 0xff)) || (data[3] != data[11]) || + (data[12] != (data[13] ^ 0xff)) || (data[12] != data[14]) || + (data[12] != (data[15] ^ 0xff)) + ) + return 1; + return 0; +} + +int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum) { + uint8_t* emCARD = BigBuf_get_EM_addr(); + uint8_t* data = emCARD + blockNum * 16; + + if (emlCheckValBl(blockNum)) + return 1; + + memcpy(blReg, data, 4); + *blBlock = data[12]; + return 0; +} + +int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum) { + uint8_t* emCARD = BigBuf_get_EM_addr(); + uint8_t* data = emCARD + blockNum * 16; + + memcpy(data + 0, &blReg, 4); + memcpy(data + 8, &blReg, 4); + blReg = blReg ^ 0xffffffff; + memcpy(data + 4, &blReg, 4); + + data[12] = blBlock; + data[13] = blBlock ^ 0xff; + data[14] = blBlock; + data[15] = blBlock ^ 0xff; + + return 0; +} + +uint64_t emlGetKey(int sectorNum, int keyType) { + uint8_t key[6] = {0x00}; + uint8_t* emCARD = BigBuf_get_EM_addr(); + memcpy(key, emCARD + 16 * (FirstBlockOfSector(sectorNum) + NumBlocksPerSector(sectorNum) - 1) + keyType * 10, 6); + return bytes_to_num(key, 6); +} + +void emlClearMem(void) { + const uint8_t trailer[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x07, 0x80, 0x69, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; + const uint8_t uid[] = {0xe6, 0x84, 0x87, 0xf3, 0x16, 0x88, 0x04, 0x00, 0x46, 0x8e, 0x45, 0x55, 0x4d, 0x70, 0x41, 0x04}; + uint8_t* emCARD = BigBuf_get_EM_addr(); + memset(emCARD, 0, CARD_MEMORY_SIZE); + + // fill sectors trailer data + for(uint16_t b = 3; b < 256; ((b < 127) ? (b += 4) : (b += 16))) + emlSetMem((uint8_t *)trailer, b, 1); + + // uid + emlSetMem((uint8_t *)uid, 0, 1); + return; +} + + +// Mifare desfire commands +int mifare_sendcmd_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing) { + uint8_t dcmd[5] = {cmd, data[0], data[1], 0x00, 0x00}; + AddCrc14A(dcmd, 3); + + ReaderTransmit(dcmd, sizeof(dcmd), NULL); + int len = ReaderReceive(answer, answer_parity); + if(!len) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed. Card timeout."); + return 1; + } + return len; +} + +int mifare_sendcmd_special2(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer,uint8_t *answer_parity, uint32_t *timing) { + uint8_t dcmd[20] = {0x00}; + dcmd[0] = cmd; + memcpy(dcmd+1,data,17); + AddCrc14A(dcmd, 18); + + ReaderTransmit(dcmd, sizeof(dcmd), NULL); + int len = ReaderReceive(answer, answer_parity); + if(!len){ + if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Authentication failed. Card timeout."); + return 1; + } + return len; +} + +int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData){ + + int len; + // load key, keynumber + uint8_t data[2]={MFDES_AUTHENTICATE, 0x00}; + uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; + + len = mifare_sendcmd_special(NULL, 1, 0x02, data, receivedAnswer,receivedAnswerPar,NULL); + if (len == 1) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Cmd Error: %02x", receivedAnswer[0]); + return 1; + } + + if (len == 12) { + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("Auth1 Resp: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + receivedAnswer[0],receivedAnswer[1],receivedAnswer[2],receivedAnswer[3],receivedAnswer[4], + receivedAnswer[5],receivedAnswer[6],receivedAnswer[7],receivedAnswer[8],receivedAnswer[9], + receivedAnswer[10],receivedAnswer[11]); + } + memcpy(blockData, receivedAnswer, 12); + return 0; + } + return 1; +} + +int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData){ + + int len; + uint8_t data[17] = {MFDES_AUTHENTICATION_FRAME}; + memcpy(data+1,key,16); + + uint8_t receivedAnswer[MAX_FRAME_SIZE] = {0x00}; + uint8_t receivedAnswerPar[MAX_PARITY_SIZE] = {0x00}; + + len = mifare_sendcmd_special2(NULL, 1, 0x03, data, receivedAnswer, receivedAnswerPar ,NULL); + + if ((receivedAnswer[0] == 0x03) && (receivedAnswer[1] == 0xae)) { + if (MF_DBGLEVEL >= MF_DBG_ERROR) + Dbprintf("Auth Error: %02x %02x", receivedAnswer[0], receivedAnswer[1]); + return 1; + } + + if (len == 12){ + if (MF_DBGLEVEL >= MF_DBG_EXTENDED) { + Dbprintf("Auth2 Resp: %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + receivedAnswer[0],receivedAnswer[1],receivedAnswer[2],receivedAnswer[3],receivedAnswer[4], + receivedAnswer[5],receivedAnswer[6],receivedAnswer[7],receivedAnswer[8],receivedAnswer[9], + receivedAnswer[10],receivedAnswer[11]); + } + memcpy(blockData, receivedAnswer, 12); + return 0; + } + return 1; +} diff --git a/armsrc/mifareutil.h b/armsrc/mifareutil.h index 268149f60..2406f9068 100644 --- a/armsrc/mifareutil.h +++ b/armsrc/mifareutil.h @@ -1,106 +1,106 @@ -//----------------------------------------------------------------------------- -// Merlok, May 2011 -// Many authors, that makes it possible -// -// 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. -//----------------------------------------------------------------------------- -// code for work with mifare cards. -//----------------------------------------------------------------------------- - -#ifndef __MIFAREUTIL_H -#define __MIFAREUTIL_H - -#include "proxmark3.h" -#include "apps.h" -#include "parity.h" -#include "util.h" -#include "string.h" -#include "iso14443crc.h" -#include "iso14443a.h" -#include "crapto1/crapto1.h" -#include "des.h" -#include "random.h" // fast_prand, prand - -// mifare authentication -#define CRYPT_NONE 0 -#define CRYPT_ALL 1 -#define CRYPT_REQUEST 2 -#define AUTH_FIRST 0 -#define AUTH_NESTED 2 - -#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) -#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication - -// mifare 4bit card answers -#define CARD_ACK 0x0A // 1010 - ACK -#define CARD_NACK_NA 0x04 // 0100 - NACK, not allowed (command not allowed) -#define CARD_NACK_TR 0x05 // 0101 - NACK, transmission error - - - -//mifare emulator states -#define MFEMUL_NOFIELD 0 -#define MFEMUL_IDLE 1 -#define MFEMUL_SELECT1 2 -#define MFEMUL_SELECT2 3 -#define MFEMUL_SELECT3 4 -#define MFEMUL_AUTH1 5 -#define MFEMUL_AUTH2 6 -#define MFEMUL_WORK 7 -#define MFEMUL_WRITEBL2 8 -#define MFEMUL_INTREG_INC 9 -#define MFEMUL_INTREG_DEC 10 -#define MFEMUL_INTREG_REST 11 -#define MFEMUL_HALTED 12 - -#define cardSTATE_TO_IDLE() cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); - +//----------------------------------------------------------------------------- +// Merlok, May 2011 +// Many authors, that makes it possible +// +// 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. +//----------------------------------------------------------------------------- +// code for work with mifare cards. +//----------------------------------------------------------------------------- + +#ifndef __MIFAREUTIL_H +#define __MIFAREUTIL_H + +#include "proxmark3.h" +#include "apps.h" +#include "parity.h" +#include "util.h" +#include "string.h" +#include "iso14443crc.h" +#include "iso14443a.h" +#include "crapto1/crapto1.h" +#include "des.h" +#include "random.h" // fast_prand, prand + +// mifare authentication +#define CRYPT_NONE 0 +#define CRYPT_ALL 1 +#define CRYPT_REQUEST 2 +#define AUTH_FIRST 0 +#define AUTH_NESTED 2 + +#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication (according to NXP documentation) +#define PRE_AUTHENTICATION_LEADTIME 400 // some (non standard) cards need a pause after select before they are ready for first authentication + +// mifare 4bit card answers +#define CARD_ACK 0x0A // 1010 - ACK +#define CARD_NACK_NA 0x04 // 0100 - NACK, not allowed (command not allowed) +#define CARD_NACK_TR 0x05 // 0101 - NACK, transmission error + + + +//mifare emulator states +#define MFEMUL_NOFIELD 0 +#define MFEMUL_IDLE 1 +#define MFEMUL_SELECT1 2 +#define MFEMUL_SELECT2 3 +#define MFEMUL_SELECT3 4 +#define MFEMUL_AUTH1 5 +#define MFEMUL_AUTH2 6 +#define MFEMUL_WORK 7 +#define MFEMUL_WRITEBL2 8 +#define MFEMUL_INTREG_INC 9 +#define MFEMUL_INTREG_DEC 10 +#define MFEMUL_INTREG_REST 11 +#define MFEMUL_HALTED 12 + +#define cardSTATE_TO_IDLE() cardSTATE = MFEMUL_IDLE; LED_B_OFF(); LED_C_OFF(); + //functions -int mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); +int mifare_sendcmd(uint8_t cmd, uint8_t *data, uint8_t data_size, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); -// mifare classic -int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); +// mifare classic +int mifare_classic_auth(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested); int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t keyType, uint64_t ui64Key, uint8_t isNested, uint32_t * ntptr, uint32_t *timing); -int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); -int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); -int mifare_classic_halt_ex(struct Crypto1State *pcs); -int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); - -// Ultralight/NTAG... -int mifare_ul_ev1_auth(uint8_t *key, uint8_t *pack); -int mifare_ultra_auth(uint8_t *key); -int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData); +int mifare_classic_readblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); +int mifare_classic_halt(struct Crypto1State *pcs, uint32_t uid); +int mifare_classic_halt_ex(struct Crypto1State *pcs); +int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t blockNo, uint8_t *blockData); + +// Ultralight/NTAG... +int mifare_ul_ev1_auth(uint8_t *key, uint8_t *pack); +int mifare_ultra_auth(uint8_t *key); +int mifare_ultra_readblock(uint8_t blockNo, uint8_t *blockData); //int mifare_ultra_writeblock_compat(uint8_t blockNo, uint8_t *blockData); int mifare_ultra_writeblock(uint8_t blockNo, uint8_t *blockData); int mifare_ultra_halt(); - -// desfire -int mifare_sendcmd_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); -int mifare_sendcmd_special2(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer,uint8_t *answer_parity, uint32_t *timing); -int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData); -int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData); + +// desfire +int mifare_sendcmd_special(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer, uint8_t *answer_parity, uint32_t *timing); +int mifare_sendcmd_special2(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, uint8_t* data, uint8_t* answer,uint8_t *answer_parity, uint32_t *timing); +int mifare_desfire_des_auth1(uint32_t uid, uint8_t *blockData); +int mifare_desfire_des_auth2(uint32_t uid, uint8_t *key, uint8_t *blockData); // crypto functions void mf_crypto1_decrypt(struct Crypto1State *pcs, uint8_t *receivedCmd, int len); -void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out); -void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par); -uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data); - -// Mifare memory structure -uint8_t NumBlocksPerSector(uint8_t sectorNo); -uint8_t FirstBlockOfSector(uint8_t sectorNo); - -// emulator functions -void emlClearMem(void); -void emlSetMem(uint8_t *data, int blockNum, int blocksCount); -void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); -void emlGetMem(uint8_t *data, int blockNum, int blocksCount); -void emlGetMemBt(uint8_t *data, int bytePtr, int byteCount); -uint64_t emlGetKey(int sectorNum, int keyType); -int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); +void mf_crypto1_decryptEx(struct Crypto1State *pcs, uint8_t *data_in, int len, uint8_t *data_out); +void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, uint8_t *par); +uint8_t mf_crypto1_encrypt4bit(struct Crypto1State *pcs, uint8_t data); + +// Mifare memory structure +uint8_t NumBlocksPerSector(uint8_t sectorNo); +uint8_t FirstBlockOfSector(uint8_t sectorNo); + +// emulator functions +void emlClearMem(void); +void emlSetMem(uint8_t *data, int blockNum, int blocksCount); +void emlSetMem_xt(uint8_t *data, int blockNum, int blocksCount, int blockBtWidth); +void emlGetMem(uint8_t *data, int blockNum, int blocksCount); +void emlGetMemBt(uint8_t *data, int bytePtr, int byteCount); +uint64_t emlGetKey(int sectorNum, int keyType); +int emlGetValBl(uint32_t *blReg, uint8_t *blBlock, int blockNum); int emlSetValBl(uint32_t blReg, uint8_t blBlock, int blockNum); int emlCheckValBl(int blockNum); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 4dd3b4b3e..da027aad6 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -1,3472 +1,3472 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2011,2012 Merlok -// -// 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 MIFARE commands -//----------------------------------------------------------------------------- - -#include "cmdhfmf.h" -#include "mifare/mifare4.h" -#include "mifare/mad.h" -#include "mifare/ndef.h" - - -#define MFBLOCK_SIZE 16 - -#define MIFARE_4K_MAXBLOCK 256 -#define MIFARE_2K_MAXBLOCK 128 -#define MIFARE_1K_MAXBLOCK 64 -#define MIFARE_MINI_MAXBLOCK 20 - -#define MIFARE_MINI_MAXSECTOR 5 -#define MIFARE_1K_MAXSECTOR 16 -#define MIFARE_2K_MAXSECTOR 32 -#define MIFARE_4K_MAXSECTOR 40 - -static int CmdHelp(const char *Cmd); - -int usage_hf14_ice(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf ice [l] [f] "); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " l nonces to be collected"); - PrintAndLogEx(NORMAL, " f save nonces to instead of hf-mf--nonces.bin"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf ice"); - PrintAndLogEx(NORMAL, " hf mf ice f nonces.bin"); - return 0; -} - -int usage_hf14_dump(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf dump [card memory] k f "); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, " k : key filename, if no given, UID will be used as filename"); - PrintAndLogEx(NORMAL, " f : data filename, if no given, UID will be used as filename"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf dump"); - PrintAndLogEx(NORMAL, " hf mf dump 4"); - return 0; -} - -int usage_hf14_mifare(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf darkside [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " (Optional) target other block"); - PrintAndLogEx(NORMAL, " (optional) target key type"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf darkside"); - PrintAndLogEx(NORMAL, " hf mf darkside 16"); - PrintAndLogEx(NORMAL, " hf mf darkside 16 B"); - return 0; -} -int usage_hf14_mf1ksim(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf sim [h] u n [i] [x] [e] [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " u (Optional) UID 4,7 or 10bytes. If not specified, the UID 4b from emulator memory will be used"); - PrintAndLogEx(NORMAL, " n (Optional) Automatically exit simulation after blocks have been read by reader. 0 = infinite"); - PrintAndLogEx(NORMAL, " i (Optional) Interactive, means that console will not be returned until simulation finishes or is aborted"); - PrintAndLogEx(NORMAL, " x (Optional) Crack, performs the 'reader attack', nr/ar attack against a reader"); - PrintAndLogEx(NORMAL, " e (Optional) Fill simulator keys from found keys"); - PrintAndLogEx(NORMAL, " v (Optional) Verbose"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf sim u 0a0a0a0a"); - PrintAndLogEx(NORMAL, " hf mf sim u 11223344556677"); - PrintAndLogEx(NORMAL, " hf mf sim u 112233445566778899AA"); - PrintAndLogEx(NORMAL, " hf mf sim u 11223344 i x"); - return 0; -} -int usage_hf14_dbg(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf dbg [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " (Optional) see list for valid levels"); - PrintAndLogEx(NORMAL, " 0 - no debug messages"); - PrintAndLogEx(NORMAL, " 1 - error messages"); - PrintAndLogEx(NORMAL, " 2 - plus information messages"); - PrintAndLogEx(NORMAL, " 3 - plus debug messages"); - PrintAndLogEx(NORMAL, " 4 - print even debug messages in timing critical functions"); - PrintAndLogEx(NORMAL, " Note: this option therefore may cause malfunction itself"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf dbg 3"); - return 0; -} -int usage_hf14_sniff(void){ - PrintAndLogEx(NORMAL, "It continuously gets data from the field and saves it to: log, emulator, emulator file."); - PrintAndLogEx(NORMAL, "Usage: hf mf sniff [h] [l] [d] [f]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " l save encrypted sequence to logfile `uid.log`"); - PrintAndLogEx(NORMAL, " d decrypt sequence and put it to log file `uid.log`"); -// PrintAndLogEx(NORMAL, " n/a e decrypt sequence, collect read and write commands and save the result of the sequence to emulator memory"); - PrintAndLogEx(NORMAL, " f decrypt sequence, collect read and write commands and save the result of the sequence to emulator dump file `uid.eml`"); - PrintAndLogEx(NORMAL, "Example:"); - PrintAndLogEx(NORMAL, " hf mf sniff l d f"); - return 0; -} -int usage_hf14_nested(void){ - PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " all sectors: hf mf nested [t,d]"); - PrintAndLogEx(NORMAL, " one sector: hf mf nested o "); - PrintAndLogEx(NORMAL, " [t]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); - PrintAndLogEx(NORMAL, " t transfer keys into emulator memory"); - PrintAndLogEx(NORMAL, " d write keys to binary file `hf-mf--key.bin`"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf nested 1 0 A FFFFFFFFFFFF "); - PrintAndLogEx(NORMAL, " hf mf nested 1 0 A FFFFFFFFFFFF t "); - PrintAndLogEx(NORMAL, " hf mf nested 1 0 A FFFFFFFFFFFF d "); - PrintAndLogEx(NORMAL, " hf mf nested o 0 A FFFFFFFFFFFF 4 A"); - return 0; -} -int usage_hf14_hardnested(void){ - PrintAndLogEx(NORMAL, "Usage:"); - PrintAndLogEx(NORMAL, " hf mf hardnested "); - PrintAndLogEx(NORMAL, " [known target key (12 hex symbols)] [w] [s]"); - PrintAndLogEx(NORMAL, " or hf mf hardnested r [known target key]"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " w acquire nonces and UID, and write them to binary file with default name hf-mf--nonces.bin"); - PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards)"); - PrintAndLogEx(NORMAL, " r read hf-mf--nonces.bin if tag present, otherwise read nonces.bin, then start attack"); - PrintAndLogEx(NORMAL, " u read/write hf-mf--nonces.bin instead of default name"); - PrintAndLogEx(NORMAL, " f read/write instead of default name"); - PrintAndLogEx(NORMAL, " t tests?"); - PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it."); - PrintAndLogEx(NORMAL, " i 5 = AVX512"); - PrintAndLogEx(NORMAL, " i 2 = AVX2"); - PrintAndLogEx(NORMAL, " i a = AVX"); - PrintAndLogEx(NORMAL, " i s = SSE2"); - PrintAndLogEx(NORMAL, " i m = MMX"); - PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf hardnested 0 A FFFFFFFFFFFF 4 A"); - PrintAndLogEx(NORMAL, " hf mf hardnested 0 A FFFFFFFFFFFF 4 A w"); - PrintAndLogEx(NORMAL, " hf mf hardnested 0 A FFFFFFFFFFFF 4 A f nonces.bin w s"); - PrintAndLogEx(NORMAL, " hf mf hardnested r"); - PrintAndLogEx(NORMAL, " hf mf hardnested r a0a1a2a3a4a5"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Add the known target key to check if it is present in the remaining key space:"); - PrintAndLogEx(NORMAL, " hf mf hardnested 0 A A0A1A2A3A4A5 4 A FFFFFFFFFFFF"); - return 0; -} -int usage_hf14_chk(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf chk [h] |<*card memory> [t|d] [] []"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " * all sectors based on card memory, other values then below defaults to 1k"); - PrintAndLogEx(NORMAL, " 0 - MINI(320 bytes)"); - PrintAndLogEx(NORMAL, " 1 - 1K"); - PrintAndLogEx(NORMAL, " 2 - 2K"); - PrintAndLogEx(NORMAL, " 4 - 4K"); - PrintAndLogEx(NORMAL, " d write keys to binary file"); - PrintAndLogEx(NORMAL, " t write keys to emulator memory\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf chk 0 A 1234567890ab keys.dic -- target block 0, Key A"); - PrintAndLogEx(NORMAL, " hf mf chk *1 ? t -- target all blocks, all keys, 1K, write to emul"); - PrintAndLogEx(NORMAL, " hf mf chk *1 ? d -- target all blocks, all keys, 1K, write to file"); - return 0; -} -int usage_hf14_chk_fast(void){ - PrintAndLogEx(NORMAL, "This is a improved checkkeys method speedwise. It checks Mifare Classic tags sector keys against a dictionary file with keys"); - PrintAndLogEx(NORMAL, "Usage: hf mf fchk [h] [t|d|f] [] []"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " all sectors based on card memory, other values than below defaults to 1k"); - PrintAndLogEx(NORMAL, " 0 - MINI(320 bytes)"); - PrintAndLogEx(NORMAL, " 1 - 1K "); - PrintAndLogEx(NORMAL, " 2 - 2K"); - PrintAndLogEx(NORMAL, " 4 - 4K"); - PrintAndLogEx(NORMAL, " d write keys to binary file"); - PrintAndLogEx(NORMAL, " t write keys to emulator memory"); - PrintAndLogEx(NORMAL, " m use dictionary from flashmemory\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf fchk 1 1234567890ab keys.dic -- target 1K using key 1234567890ab, using dictionary file"); - PrintAndLogEx(NORMAL, " hf mf fchk 1 t -- target 1K, write to emulator memory"); - PrintAndLogEx(NORMAL, " hf mf fchk 1 d -- target 1K, write to file"); -#ifdef WITH_FLASH - PrintAndLogEx(NORMAL, " hf mf fchk 1 m -- target 1K, use dictionary from flashmemory"); -#endif - return 0; -} -int usage_hf14_keybrute(void){ - PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); - PrintAndLogEx(NORMAL, "You have a known 4 last bytes of a key recovered with mf_nonce_brute tool."); - PrintAndLogEx(NORMAL, "First 2 bytes of key will be bruteforced"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, " ---[ This attack is obsolete, try hardnested instead ]---"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf keybrute [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " target block number"); - PrintAndLogEx(NORMAL, " target key type"); - PrintAndLogEx(NORMAL, " candidate key from mf_nonce_brute tool"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf keybrute 1 A 000011223344"); - return 0; -} -int usage_hf14_restore(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf restore [card memory] u k f "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, " u : uid, try to restore from hf-mf--key.bin and hf-mf--data.bin"); - PrintAndLogEx(NORMAL, " k : key filename, specific the full filename of key file"); - PrintAndLogEx(NORMAL, " f : data filename, specific the full filename of data file"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf restore -- read the UID from tag first, then restore from hf-mf--key.bin and and hf-mf--data.bin"); - PrintAndLogEx(NORMAL, " hf mf restore 1 u 12345678 -- restore from hf-mf-12345678-key.bin and hf-mf-12345678-data.bin"); - PrintAndLogEx(NORMAL, " hf mf restore 1 u 12345678 k dumpkey.bin -- restore from dumpkey.bin and hf-mf-12345678-data.bin"); - PrintAndLogEx(NORMAL, " hf mf restore 4 -- read the UID from tag with 4K memory first, then restore from hf-mf--key.bin and and hf-mf--data.bin"); - return 0; -} -int usage_hf14_decryptbytes(void){ - PrintAndLogEx(NORMAL, "Decrypt Crypto-1 encrypted bytes given some known state of crypto. See tracelog to gather needed values\n"); - PrintAndLogEx(NORMAL, "Usage: hf mf decrypt [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " reader nonce"); - PrintAndLogEx(NORMAL, " encrypted reader response"); - PrintAndLogEx(NORMAL, " encrypted tag response"); - PrintAndLogEx(NORMAL, " encrypted data, taken directly after at_enc and forward"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf decrypt b830049b 9248314a 9280e203 41e586f9\n"); - PrintAndLogEx(NORMAL, " this sample decrypts 41e586f9 -> 3003999a Annotated: 30 03 [99 9a] auth block 3 [crc]"); - return 0; -} - -int usage_hf14_eget(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf eget "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf eget 0 "); - return 0; -} -int usage_hf14_eclr(void){ - PrintAndLogEx(NORMAL, "It set card emulator memory to empty data blocks and key A/B FFFFFFFFFFFF \n"); - PrintAndLogEx(NORMAL, "Usage: hf mf eclr"); - return 0; -} -int usage_hf14_eset(void){ - PrintAndLogEx(NORMAL, "Usage: hf mf eset "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf eset 1 000102030405060708090a0b0c0d0e0f "); - return 0; -} -int usage_hf14_eload(void){ - PrintAndLogEx(NORMAL, "It loads emul dump from the file `filename.eml`"); - PrintAndLogEx(NORMAL, "Usage: hf mf eload [card memory] [numblocks]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K, u = UL"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf eload filename"); - PrintAndLogEx(NORMAL, " hf mf eload 4 filename"); - return 0; -} -int usage_hf14_esave(void){ - PrintAndLogEx(NORMAL, "It saves emul dump into the file `filename.eml` or `cardID.eml`"); - PrintAndLogEx(NORMAL, " Usage: hf mf esave [card memory] [file name w/o `.eml`]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf esave "); - PrintAndLogEx(NORMAL, " hf mf esave 4"); - PrintAndLogEx(NORMAL, " hf mf esave 4 filename"); - return 0; -} -int usage_hf14_ecfill(void){ - PrintAndLogEx(NORMAL, "Read card and transfer its data to emulator memory."); - PrintAndLogEx(NORMAL, "Keys must be laid in the emulator memory. \n"); - PrintAndLogEx(NORMAL, "Usage: hf mf ecfill [card memory]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf ecfill A"); - PrintAndLogEx(NORMAL, " hf mf ecfill A 4"); - return 0; -} -int usage_hf14_ekeyprn(void){ - PrintAndLogEx(NORMAL, "It prints the keys loaded in the emulator memory"); - PrintAndLogEx(NORMAL, "Usage: hf mf ekeyprn [card memory]"); - PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf ekeyprn 1"); - return 0; -} - -int usage_hf14_csetuid(void){ - PrintAndLogEx(NORMAL, "Set UID, ATQA, and SAK for magic Chinese card. Only works with magic cards"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf csetuid [h] [ATQA 4 hex symbols] [SAK 2 hex symbols] [w]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " w wipe card before writing"); - PrintAndLogEx(NORMAL, " UID 8 hex symbols"); - PrintAndLogEx(NORMAL, " ATQA 4 hex symbols"); - PrintAndLogEx(NORMAL, " SAK 2 hex symbols"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf csetuid 01020304"); - PrintAndLogEx(NORMAL, " hf mf csetuid 01020304 0004 08 w"); - return 0; -} -int usage_hf14_csetblk(void){ - PrintAndLogEx(NORMAL, "Set block data for magic Chinese card. Only works with magic cards"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf csetblk [h] [w]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " w wipe card before writing"); - PrintAndLogEx(NORMAL, " block number"); - PrintAndLogEx(NORMAL, " block data to write (32 hex symbols)"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf csetblk 1 01020304050607080910111213141516"); - PrintAndLogEx(NORMAL, " hf mf csetblk 1 01020304050607080910111213141516 w"); - return 0; -} -int usage_hf14_cload(void){ - PrintAndLogEx(NORMAL, "It loads magic Chinese card from the file `filename.eml`"); - PrintAndLogEx(NORMAL, "or from emulator memory"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf cload [h] [e] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " e load card with data from emulator memory"); - PrintAndLogEx(NORMAL, " j load card with data from json file"); - PrintAndLogEx(NORMAL, " b load card with data from binary file"); - PrintAndLogEx(NORMAL, " load card with data from eml file"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf cload mydump"); - PrintAndLogEx(NORMAL, " hf mf cload e"); - return 0; -} -int usage_hf14_cgetblk(void){ - PrintAndLogEx(NORMAL, "Get block data from magic Chinese card. Only works with magic cards\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf cgetblk [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " block number"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf cgetblk 1"); - return 0; -} -int usage_hf14_cgetsc(void){ - PrintAndLogEx(NORMAL, "Get sector data from magic Chinese card. Only works with magic cards\n"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf cgetsc [h] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " sector number"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf cgetsc 0"); - return 0; -} -int usage_hf14_csave(void){ - PrintAndLogEx(NORMAL, "It saves `magic Chinese` card dump into the file `filename.eml` or `cardID.eml`"); - PrintAndLogEx(NORMAL, "or into emulator memory"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf csave [h] [e] [u] [card memory] i "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " e save data to emulator memory"); - PrintAndLogEx(NORMAL, " u save data to file, use carduid as filename"); - PrintAndLogEx(NORMAL, " card memory 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); - PrintAndLogEx(NORMAL, " o save data to file"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf csave u 1"); - PrintAndLogEx(NORMAL, " hf mf csave e 1"); - PrintAndLogEx(NORMAL, " hf mf csave 4 o filename"); - return 0; -} -int usage_hf14_nack(void) { - PrintAndLogEx(NORMAL, "Test a mifare classic based card for the NACK bug."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Usage: hf mf nack [h] [v]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h this help"); - PrintAndLogEx(NORMAL, " v verbose"); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf nack"); - return 0; -} - -int GetHFMF14AUID(uint8_t *uid, int *uidlen) { - UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { - PrintAndLogEx(WARNING, "iso14443a card select failed"); - DropField(); - return 0; - } - - iso14a_card_select_t card; - memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); - memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t)); - *uidlen = card.uidlen; - return 1; -} - -char * GenerateFilename(const char *prefix, const char *suffix){ - uint8_t uid[10] = {0,0,0,0,0,0,0,0,0,0}; - int uidlen=0; - char * fptr = calloc (sizeof (char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid)*2 + 1, sizeof(uint8_t)); - - GetHFMF14AUID(uid, &uidlen); - if (!uidlen) { - PrintAndLogEx(WARNING, "No tag found."); - free(fptr); - return NULL; - } - - strcpy(fptr, prefix); - FillFileNameByUID(fptr, uid, suffix, uidlen); - return fptr; -} - -int CmdHF14AMfDarkside(const char *Cmd) { - uint8_t blockno = 0, key_type = MIFARE_AUTH_KEYA; - uint64_t key = 0; - - char cmdp = tolower(param_getchar(Cmd, 0)); - if ( cmdp == 'h' ) return usage_hf14_mifare(); - - blockno = param_get8(Cmd, 0); - - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 'b') - key_type = MIFARE_AUTH_KEYB; - - int isOK = mfDarkside(blockno, key_type, &key); - PrintAndLogEx(NORMAL, ""); - switch (isOK) { - case -1 : PrintAndLogEx(WARNING, "button pressed. Aborted."); return 1; - case -2 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)."); return 1; - case -3 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator is not predictable)."); return 1; - case -4 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown"); - PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour."); return 1; - case -5 : PrintAndLogEx(WARNING, "aborted via keyboard."); return 1; - default : PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 "\n", key); break; - } - PrintAndLogEx(NORMAL, ""); - return 0; -} - -int CmdHF14AMfWrBl(const char *Cmd) { - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t bldata[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; - char cmdp = 0x00; - - if (strlen(Cmd) < 3) { - PrintAndLogEx(NORMAL, "Usage: hf mf wrbl "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf wrbl 0 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F"); - return 0; - } - - blockNo = param_get8(Cmd, 0); - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 0x00) { - PrintAndLogEx(NORMAL, "Key type must be A or B"); - return 1; - } - - if (cmdp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 2, key, 12)) { - PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); - return 1; - } - - if (param_gethex(Cmd, 3, bldata, 32)) { - PrintAndLogEx(NORMAL, "Block data must include 32 HEX symbols"); - return 1; - } - - PrintAndLogEx(NORMAL, "--block no:%d, key type:%c, key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); - PrintAndLogEx(NORMAL, "--data: %s", sprint_hex(bldata, 16)); - - UsbCommand c = {CMD_MIFARE_WRITEBL, {blockNo, keyType, 0}}; - memcpy(c.d.asBytes, key, 6); - memcpy(c.d.asBytes + 10, bldata, 16); - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.arg[0] & 0xff; - PrintAndLogEx(NORMAL, "isOk:%02x", isOK); - } else { - PrintAndLogEx(NORMAL, "Command execute timeout"); - } - - return 0; -} - -int CmdHF14AMfRdBl(const char *Cmd) { - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - char cmdp = 0x00; - - if (strlen(Cmd)<3) { - PrintAndLogEx(NORMAL, "Usage: hf mf rdbl "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf rdbl 0 A FFFFFFFFFFFF "); - return 0; - } - - blockNo = param_get8(Cmd, 0); - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp == 0x00) { - PrintAndLogEx(NORMAL, "Key type must be A or B"); - return 1; - } - - if (cmdp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 2, key, 12)) { - PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); - return 1; - } - PrintAndLogEx(NORMAL, "--block no:%d, key type:%c, key:%s ", blockNo, keyType?'B':'A', sprint_hex(key, 6)); - - UsbCommand c = {CMD_MIFARE_READBL, {blockNo, keyType, 0}}; - memcpy(c.d.asBytes, key, 6); - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t isOK = resp.arg[0] & 0xff; - uint8_t *data = resp.d.asBytes; - - if (isOK) { - PrintAndLogEx(NORMAL, "isOk:%02x data:%s", isOK, sprint_hex(data, 16)); - } else { - PrintAndLogEx(NORMAL, "isOk:%02x", isOK); - return 1; - } - - if (mfIsSectorTrailer(blockNo) && (data[6] || data[7] || data[8])) { - PrintAndLogEx(NORMAL, "Trailer decoded:"); - int bln = mfFirstBlockOfSector(mfSectorNum(blockNo)); - int blinc = (mfNumBlocksPerSector(mfSectorNum(blockNo)) > 4) ? 5 : 1; - for (int i = 0; i < 4; i++) { - PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &data[6])); - bln += blinc; - } - PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&data[9], 1)); - } - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - return 2; - } - - return 0; -} - -int CmdHF14AMfRdSc(const char *Cmd) { - int i; - uint8_t sectorNo = 0; - uint8_t keyType = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t isOK = 0; - uint8_t *data = NULL; - char cmdp = 0x00; - - if (strlen(Cmd) < 3) { - PrintAndLogEx(NORMAL, "Usage: hf mf rdsc "); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " hf mf rdsc 0 A FFFFFFFFFFFF "); - return 0; - } - - sectorNo = param_get8(Cmd, 0); - if (sectorNo > MIFARE_4K_MAXSECTOR ) { - PrintAndLogEx(NORMAL, "Sector number must be less than 40"); - return 1; - } - - cmdp = tolower(param_getchar(Cmd, 1)); - if (cmdp != 'a' && cmdp != 'b') { - PrintAndLogEx(NORMAL, "Key type must be A or B"); - return 1; - } - - if (cmdp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 2, key, 12)) { - PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); - return 1; - } - PrintAndLogEx(NORMAL, "--sector no:%d key type:%c key:%s ", sectorNo, keyType?'B':'A', sprint_hex(key, 6)); - - UsbCommand c = {CMD_MIFARE_READSC, {sectorNo, keyType, 0}}; - memcpy(c.d.asBytes, key, 6); - clearCommandBuffer(); - SendCommand(&c); - PrintAndLogEx(NORMAL, ""); - - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - isOK = resp.arg[0] & 0xff; - data = resp.d.asBytes; - - PrintAndLogEx(NORMAL, "isOk:%02x", isOK); - if (isOK) { - for (i = 0; i < (sectorNo<32?3:15); i++) { - PrintAndLogEx(NORMAL, "data : %s", sprint_hex(data + i * 16, 16)); - } - PrintAndLogEx(NORMAL, "trailer: %s", sprint_hex(data + (sectorNo<32?3:15) * 16, 16)); - - PrintAndLogEx(NORMAL, "Trailer decoded:"); - int bln = mfFirstBlockOfSector(sectorNo); - int blinc = (mfNumBlocksPerSector(sectorNo) > 4) ? 5 : 1; - for (i = 0; i < 4; i++) { - PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &(data + (sectorNo<32?3:15) * 16)[6])); - bln += blinc; - } - PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&(data + (sectorNo<32?3:15) * 16)[9], 1)); - } - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - - return 0; -} - -uint16_t NumOfBlocks(char card){ - switch(card){ - case '0' : return MIFARE_MINI_MAXBLOCK; - case '1' : return MIFARE_1K_MAXBLOCK; - case '2' : return MIFARE_2K_MAXBLOCK; - case '4' : return MIFARE_4K_MAXBLOCK; - default : return MIFARE_1K_MAXBLOCK; - } -} -uint8_t NumOfSectors(char card){ - switch(card){ - case '0' : return MIFARE_MINI_MAXSECTOR; - case '1' : return MIFARE_1K_MAXSECTOR; - case '2' : return MIFARE_2K_MAXSECTOR; - case '4' : return MIFARE_4K_MAXSECTOR; - default : return MIFARE_1K_MAXSECTOR; - } -} - -uint8_t FirstBlockOfSector(uint8_t sectorNo) { - if (sectorNo < 32) { - return sectorNo * 4; - } else { - return 32 * 4 + (sectorNo - 32) * 16; - } -} - -uint8_t NumBlocksPerSector(uint8_t sectorNo) { - if (sectorNo < 32) { - return 4; - } else { - return 16; - } -} - -int CmdHF14AMfDump(const char *Cmd) { - - uint8_t sectorNo, blockNo; - uint8_t keyA[40][6]; - uint8_t keyB[40][6]; - uint8_t rights[40][4]; - uint8_t carddata[256][16]; - uint8_t numSectors = 16; - uint8_t cmdp = 0; - - char keyFilename[FILE_PATH_SIZE] = {0}; - char dataFilename[FILE_PATH_SIZE]; - char * fptr; - - memset(keyFilename, 0, sizeof(keyFilename)); - memset(dataFilename, 0, sizeof(dataFilename)); - - FILE *f; - UsbCommand resp; - - while(param_getchar(Cmd, cmdp) != 0x00) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf14_dump(); - case 'k': - param_getstr(Cmd, cmdp+1, keyFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - case 'f': - param_getstr(Cmd, cmdp+1, dataFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - default: - if (cmdp == 0) { - numSectors = NumOfSectors(param_getchar(Cmd, cmdp)); - cmdp++; - } else { - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - return usage_hf14_dump(); - } - } - } - - if ( keyFilename[0] == 0x00 ) { - fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) - return 1; - - strcpy(keyFilename, fptr); - } - - if ((f = fopen(keyFilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_(%s), keyFilename); - return 1; - } - - // Read keys A from file - size_t bytes_read; - for (sectorNo=0; sectorNo> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0 - rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1 - rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2 - rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer - break; - } else if (tries == 2) { // on last try set defaults - PrintAndLogEx(FAILED, "could not get access rights for sector %2d. Trying with defaults...", sectorNo); - rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; - rights[sectorNo][3] = 0x01; - } - } else { - PrintAndLogEx(FAILED, "command execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo); - rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; - rights[sectorNo][3] = 0x01; - } - } - } - - PrintAndLogEx(SUCCESS, "Finished reading sector access bits"); - PrintAndLogEx(INFO, "Dumping all blocks from card..."); - - bool isOK = true; - for (sectorNo = 0; isOK && sectorNo < numSectors; sectorNo++) { - for (blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { - bool received = false; - - for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { - if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer. At least the Access Conditions can always be read with key A. - UsbCommand c = {CMD_MIFARE_READBL, {FirstBlockOfSector(sectorNo) + blockNo, 0, 0}}; - memcpy(c.d.asBytes, keyA[sectorNo], 6); - clearCommandBuffer(); - SendCommand(&c); - received = WaitForResponseTimeout(CMD_ACK, &resp, 1500); - } else { // data block. Check if it can be read with key A or key B - uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo/5; - if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { // only key B would work - UsbCommand c = {CMD_MIFARE_READBL, {FirstBlockOfSector(sectorNo) + blockNo, 1, 0}}; - memcpy(c.d.asBytes, keyB[sectorNo], 6); - SendCommand(&c); - received = WaitForResponseTimeout(CMD_ACK, &resp, 1500); - } else if (rights[sectorNo][data_area] == 0x07) { // no key would work - isOK = false; - PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d", sectorNo, blockNo); - tries = MIFARE_SECTOR_RETRY; - } else { // key A would work - UsbCommand c = {CMD_MIFARE_READBL, {FirstBlockOfSector(sectorNo) + blockNo, 0, 0}}; - memcpy(c.d.asBytes, keyA[sectorNo], 6); - clearCommandBuffer(); - SendCommand(&c); - received = WaitForResponseTimeout(CMD_ACK, &resp, 1500); - } - } - if (received) { - isOK = resp.arg[0] & 0xff; - if (isOK) break; - } - } - - if (received) { - isOK = resp.arg[0] & 0xff; - uint8_t *data = resp.d.asBytes; - if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer. Fill in the keys. - data[0] = (keyA[sectorNo][0]); - data[1] = (keyA[sectorNo][1]); - data[2] = (keyA[sectorNo][2]); - data[3] = (keyA[sectorNo][3]); - data[4] = (keyA[sectorNo][4]); - data[5] = (keyA[sectorNo][5]); - data[10] = (keyB[sectorNo][0]); - data[11] = (keyB[sectorNo][1]); - data[12] = (keyB[sectorNo][2]); - data[13] = (keyB[sectorNo][3]); - data[14] = (keyB[sectorNo][4]); - data[15] = (keyB[sectorNo][5]); - } - if (isOK) { - memcpy(carddata[FirstBlockOfSector(sectorNo) + blockNo], data, 16); - PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo); - } else { - PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo); - break; - } - } - else { - isOK = false; - PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo); - break; - } - } - } - - if (isOK == 0) { - PrintAndLogEx(FAILED, "Something went wrong"); - return 0; - } - - PrintAndLogEx(SUCCESS, "\nSuccedded in dumping all blocks"); - - if ( strlen(dataFilename) < 1 ) { - fptr = dataFilename; - fptr += sprintf(fptr, "hf-mf-"); - FillFileNameByUID(fptr, (uint8_t *)carddata, "-data", 4); - } - - uint16_t bytes = 16*(FirstBlockOfSector(numSectors - 1) + NumBlocksPerSector(numSectors - 1)); - - saveFile(dataFilename, "bin", (uint8_t *)carddata, bytes); - saveFileEML(dataFilename, "eml", (uint8_t *)carddata, bytes, MFBLOCK_SIZE); - saveFileJSON(dataFilename, "json", jsfCardMemory, (uint8_t *)carddata, bytes); - return 0; -} - -int CmdHF14AMfRestore(const char *Cmd) { - uint8_t sectorNo,blockNo; - uint8_t keyType = 0; - uint8_t key[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; - uint8_t bldata[16] = {0x00}; - uint8_t keyA[40][6]; - uint8_t keyB[40][6]; - uint8_t numSectors = 16; - uint8_t cmdp = 0; - char keyFilename[FILE_PATH_SIZE] = ""; - char dataFilename[FILE_PATH_SIZE] = ""; - char szTemp[FILE_PATH_SIZE-20] = ""; - char *fptr; - FILE *fdump, *fkeys; - - while(param_getchar(Cmd, cmdp) != 0x00) { - switch(tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_hf14_restore(); - case 'u': - param_getstr(Cmd, cmdp+1, szTemp, FILE_PATH_SIZE-20); - if(keyFilename[0]==0x00) - snprintf(keyFilename, FILE_PATH_SIZE, "hf-mf-%s-key.bin", szTemp); - if(dataFilename[0]==0x00) - snprintf(dataFilename, FILE_PATH_SIZE, "hf-mf-%s-data.bin", szTemp); - cmdp+=2; - break; - case 'k': - param_getstr(Cmd, cmdp+1, keyFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - case 'f': - param_getstr(Cmd, cmdp+1, dataFilename, FILE_PATH_SIZE); - cmdp += 2; - break; - default: - if ( cmdp == 0 ) { - numSectors = NumOfSectors(param_getchar(Cmd, cmdp)); - cmdp++; - } else { - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); - return usage_hf14_restore(); - } - } - } - - if ( keyFilename[0] == 0x00 ) { - fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) - return 1; - - strcpy(keyFilename, fptr); - } - - if ((fkeys = fopen(keyFilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_(%s), keyFilename); - return 1; - } - - size_t bytes_read; - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - bytes_read = fread( keyA[sectorNo], 1, 6, fkeys ); - if ( bytes_read != 6 ) { - PrintAndLogEx(WARNING, "File reading error " _YELLOW_(%s), keyFilename); - fclose(fkeys); - return 2; - } - } - - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - bytes_read = fread( keyB[sectorNo], 1, 6, fkeys ); - if ( bytes_read != 6 ) { - PrintAndLogEx(WARNING, "File reading error " _YELLOW_(%s), keyFilename); - fclose(fkeys); - return 2; - } - } - - fclose(fkeys); - - if ( dataFilename[0] == 0x00 ) { - fptr = GenerateFilename("hf-mf-", "-data.bin"); - if (fptr == NULL) - return 1; - - strcpy(dataFilename,fptr); - } - - if ((fdump = fopen(dataFilename, "rb")) == NULL) { - PrintAndLogEx(WARNING, "Could not find file " _YELLOW_(%s), dataFilename); - return 1; - } - PrintAndLogEx(INFO, "Restoring " _YELLOW_(%s)" to card", dataFilename); - - for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { - for (blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { - UsbCommand c = {CMD_MIFARE_WRITEBL, {FirstBlockOfSector(sectorNo) + blockNo, keyType, 0}}; - memcpy(c.d.asBytes, key, 6); - bytes_read = fread(bldata, 1, 16, fdump); - if ( bytes_read != 16) { - PrintAndLogEx(WARNING, "File reading error " _YELLOW_(%s), dataFilename); - fclose(fdump); - fdump = NULL; - return 2; - } - - if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer - bldata[0] = (keyA[sectorNo][0]); - bldata[1] = (keyA[sectorNo][1]); - bldata[2] = (keyA[sectorNo][2]); - bldata[3] = (keyA[sectorNo][3]); - bldata[4] = (keyA[sectorNo][4]); - bldata[5] = (keyA[sectorNo][5]); - bldata[10] = (keyB[sectorNo][0]); - bldata[11] = (keyB[sectorNo][1]); - bldata[12] = (keyB[sectorNo][2]); - bldata[13] = (keyB[sectorNo][3]); - bldata[14] = (keyB[sectorNo][4]); - bldata[15] = (keyB[sectorNo][5]); - } - - PrintAndLogEx(NORMAL, "Writing to block %3d: %s", FirstBlockOfSector(sectorNo) + blockNo, sprint_hex(bldata, 16)); - - memcpy(c.d.asBytes + 10, bldata, 16); - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { - uint8_t isOK = resp.arg[0] & 0xff; - PrintAndLogEx(SUCCESS, "isOk:%02x", isOK); - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - } - } - - fclose(fdump); - return 0; -} - -int CmdHF14AMfNested(const char *Cmd) { - int i, res, iterations; - sector_t *e_sector = NULL; - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t trgBlockNo = 0; - uint8_t trgKeyType = 0; - uint8_t SectorsCnt = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t keyBlock[(MIFARE_DEFAULTKEYS_SIZE + 1) *6]; - uint64_t key64 = 0; - bool transferToEml = false; - bool createDumpFile = false; - FILE *fkeys; - uint8_t standart[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - char *fptr; - - if (strlen(Cmd) < 3) return usage_hf14_nested(); - - char cmdp, ctmp; - cmdp = tolower(param_getchar(Cmd, 0)); - blockNo = param_get8(Cmd, 1); - ctmp = tolower(param_getchar(Cmd, 2)); - - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "key type must be A or B"); - return 1; - } - - if (ctmp != 'a') - keyType = 1; - - if (param_gethex(Cmd, 3, key, 12)) { - PrintAndLogEx(WARNING, "key must include 12 HEX symbols"); - return 1; - } - - if (cmdp == 'o') { - trgBlockNo = param_get8(Cmd, 4); - ctmp = tolower(param_getchar(Cmd, 5)); - if (ctmp != 'a' && ctmp != 'b') { - PrintAndLogEx(WARNING, "target key type must be A or B"); - return 1; - } - if (ctmp != 'a') { - trgKeyType = 1; - } - } else { - SectorsCnt = NumOfSectors(cmdp); - } - - uint8_t j = 4; - while ( ctmp != 0x00 ) { - - ctmp = tolower(param_getchar(Cmd, j)); - transferToEml |= (ctmp == 't'); - createDumpFile |= (ctmp == 'd'); - - j++; - } - - // check if we can authenticate to sector - res = mfCheckKeys(blockNo, keyType, true, 1, key, &key64); - if (res) { - PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); - return 3; - } - - if (cmdp == 'o') { - int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true); - switch (isOK) { - case -1 : PrintAndLogEx(WARNING, "Error: No response from Proxmark.\n"); break; - case -2 : PrintAndLogEx(WARNING, "Button pressed. Aborted.\n"); break; - case -3 : PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break; - case -4 : PrintAndLogEx(FAILED, "No valid key found"); break; - case -5 : - key64 = bytes_to_num(keyBlock, 6); - - // transfer key to the emulator - if (transferToEml) { - uint8_t sectortrailer; - if (trgBlockNo < 32*4) { // 4 block sector - sectortrailer = trgBlockNo | 0x03; - } else { // 16 block sector - sectortrailer = trgBlockNo | 0x0f; - } - mfEmlGetMem(keyBlock, sectortrailer, 1); - - if (!trgKeyType) - num_to_bytes(key64, 6, keyBlock); - else - num_to_bytes(key64, 6, &keyBlock[10]); - mfEmlSetMem(keyBlock, sectortrailer, 1); - PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); - } - return 0; - default : PrintAndLogEx(WARNING, "Unknown Error.\n"); - } - return 2; - } - else { // ------------------------------------ multiple sectors working - uint64_t t1 = msclock(); - - e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) return 1; - - //test current key and additional standard keys first - // add parameter key - memcpy( keyBlock + (MIFARE_DEFAULTKEYS_SIZE * 6), key, 6 ); - - for (int cnt = 0; cnt < MIFARE_DEFAULTKEYS_SIZE; cnt++){ - num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t*)(keyBlock + cnt * 6)); - } - - PrintAndLogEx(SUCCESS, "Testing known keys. Sector count=%d", SectorsCnt); - res = mfCheckKeys_fast( SectorsCnt, true, true, 1, MIFARE_DEFAULTKEYS_SIZE + 1, keyBlock, e_sector, false); - - uint64_t t2 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time to check %d known keys: %.0f seconds\n", MIFARE_DEFAULTKEYS_SIZE, (float)t2/1000.0 ); - PrintAndLogEx(SUCCESS, "enter nested attack"); - - // nested sectors - iterations = 0; - bool calibrate = true; - - for (i = 0; i < MIFARE_SECTOR_RETRY; i++) { - for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { - for (trgKeyType = 0; trgKeyType < 2; ++trgKeyType) { - - if (e_sector[sectorNo].foundKey[trgKeyType]) continue; - - int16_t isOK = mfnested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); - switch (isOK) { - case -1 : PrintAndLogEx(WARNING, "error: No response from Proxmark.\n"); break; - case -2 : PrintAndLogEx(WARNING, "button pressed. Aborted.\n"); break; - case -3 : PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break; - case -4 : //key not found - calibrate = false; - iterations++; - continue; - case -5 : - calibrate = false; - iterations++; - e_sector[sectorNo].foundKey[trgKeyType] = 1; - e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); - - res = mfCheckKeys_fast( SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false); - continue; - - default : PrintAndLogEx(WARNING, "unknown Error.\n"); - } - free(e_sector); - return 2; - } - } - } - - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "time in nested: %.0f seconds\n", (float)t1/1000.0); - - - // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - PrintAndLogEx(INFO, "trying to read key B..."); - for (i = 0; i < SectorsCnt; i++) { - // KEY A but not KEY B - if ( e_sector[i].foundKey[0] && !e_sector[i].foundKey[1] ) { - - uint8_t sectrail = (FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); - - PrintAndLogEx(SUCCESS, "reading block %d", sectrail); - - UsbCommand c = {CMD_MIFARE_READBL, {sectrail, 0, 0}}; - num_to_bytes(e_sector[i].Key[0], 6, c.d.asBytes); // KEY A - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - if ( !WaitForResponseTimeout(CMD_ACK,&resp,1500)) continue; - - uint8_t isOK = resp.arg[0] & 0xff; - if (!isOK) continue; - - uint8_t *data = resp.d.asBytes; - key64 = bytes_to_num(data+10, 6); - if (key64) { - PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data+10, 6)); - e_sector[i].foundKey[1] = true; - e_sector[i].Key[1] = key64; - } - } - } - - - //print them - printKeyTable( SectorsCnt, e_sector ); - - // transfer them to the emulator - if (transferToEml) { - for (i = 0; i < SectorsCnt; i++) { - mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); - if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, keyBlock); - if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); - mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); - } - PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); - } - - // Create dump file - if (createDumpFile) { - fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) { - free(e_sector); - return 1; - } - - if ((fkeys = fopen(fptr, "wb")) == NULL) { - PrintAndLogEx(WARNING, "could not create file " _YELLOW_(%s), fptr); - free(e_sector); - return 1; - } - - PrintAndLogEx(SUCCESS, "saving keys to binary file " _YELLOW_(%s), fptr); - for (i=0; i= FILE_PATH_SIZE ) { - PrintAndLogEx(FAILED, "Filename too long"); - continue; - } - - f = fopen( filename, "r"); - if ( !f ){ - PrintAndLogEx(FAILED, "File: " _YELLOW_(%s) ": not found or locked.", filename); - continue; - } - - // read file - while( fgets(buf, sizeof(buf), f) ){ - if (strlen(buf) < 12 || buf[11] == '\n') - continue; - - while (fgetc(f) != '\n' && !feof(f)) ; //goto next line - - if( buf[0]=='#' ) continue; //The line start with # is comment, skip - - if (!isxdigit(buf[0])){ - PrintAndLogEx(FAILED, "File content error. '" _YELLOW_(%s)"' must include 12 HEX symbols", buf); - continue; - } - - buf[12] = 0; - if ( keyitems - keycnt < 2) { - p = realloc(keyBlock, 6 * (keyitems += 64)); - if (!p) { - PrintAndLogEx(FAILED, "Cannot allocate memory for default keys"); - free(keyBlock); - fclose(f); - return 2; - } - keyBlock = p; - } - int pos = 6 * keycnt; - memset(keyBlock + pos, 0, 6); - num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + pos); - keycnt++; - memset(buf, 0, sizeof(buf)); - } - fclose(f); - PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_(%s), keycnt, filename); - } - } - - if (keycnt == 0 && !use_flashmemory) { - PrintAndLogEx(SUCCESS, "No key specified, trying default keys"); - for (;keycnt < MIFARE_DEFAULTKEYS_SIZE; keycnt++) - PrintAndLogEx(NORMAL, "[%2d] %02x%02x%02x%02x%02x%02x", keycnt, - (keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], - (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5]); - } - - // // initialize storage for found keys - e_sector = calloc(sectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) { - free(keyBlock); - return 1; - } - - uint32_t chunksize = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; - bool firstChunk = true, lastChunk = false; - - // time - uint64_t t1 = msclock(); - - if ( use_flashmemory ) { - PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); - mfCheckKeys_fast( sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory); - } else { - - // strategys. 1= deep first on sector 0 AB, 2= width first on all sectors - for (uint8_t strategy = 1; strategy < 3; strategy++) { - PrintAndLogEx(SUCCESS, "Running strategy %u", strategy); - - // main keychunk loop - for (uint32_t i = 0; i < keycnt; i += chunksize) { - - if (ukbhit()) { - int gc = getchar(); (void)gc; - PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); - goto out; - } - - uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; - - // last chunk? - if ( size == keycnt - i) - lastChunk = true; - - int res = mfCheckKeys_fast( sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); - - if ( firstChunk ) - firstChunk = false; - - // all keys, aborted - if ( res == 0 || res == 2 ) - goto out; - } // end chunks of keys - firstChunk = true; - lastChunk = false; - } // end strategy - } -out: - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "Time in checkkeys (fast): %.1fs\n", (float)(t1/1000.0)); - - // check.. - uint8_t found_keys = 0; - for (uint8_t i = 0; i < sectorsCnt; ++i) { - - if ( e_sector[i].foundKey[0] ) - found_keys++; - - if ( e_sector[i].foundKey[1] ) - found_keys++; - } - - if ( found_keys == 0 ) { - PrintAndLogEx(WARNING, "No keys found"); - } else { - - printKeyTable( sectorsCnt, e_sector ); - - if (transferToEml) { - uint8_t block[16] = {0x00}; - for (uint8_t i = 0; i < sectorsCnt; ++i ) { - mfEmlGetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); - if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, block); - if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, block+10); - mfEmlSetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); - } - PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); - } - - if (createDumpFile) { - fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) - return 1; - - FILE *fkeys = fopen(fptr, "wb"); - if (fkeys == NULL) { - PrintAndLogEx(WARNING, "Could not create file " _YELLOW_(%s), fptr); - free(keyBlock); - free(e_sector); - return 1; - } - PrintAndLogEx(SUCCESS, "Printing keys to binary file " _YELLOW_(%s)"...", fptr); - - for (i=0; i 0xffffffffffff has been inserted for unknown keys.", fptr); - } - } - - free(keyBlock); - free(e_sector); - PrintAndLogEx(NORMAL, ""); - return 0; -} - -int CmdHF14AMfChk(const char *Cmd) { - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 3 || ctmp == 'h') return usage_hf14_chk(); - - FILE * f; - char filename[FILE_PATH_SIZE] = {0}; - char buf[13]; - uint8_t *keyBlock = NULL, *p; - sector_t *e_sector = NULL; - - uint8_t blockNo = 0; - uint8_t SectorsCnt = 1; - uint8_t keyType = 0; - uint32_t keyitems = MIFARE_DEFAULTKEYS_SIZE; - uint64_t key64 = 0; - uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - char *fptr; - int clen = 0; - int transferToEml = 0; - int createDumpFile = 0; - int i, res, keycnt = 0; - - keyBlock = calloc(MIFARE_DEFAULTKEYS_SIZE, 6); - if (keyBlock == NULL) return 1; - - for (int cnt = 0; cnt < MIFARE_DEFAULTKEYS_SIZE; cnt++) - num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t*)(keyBlock + cnt * 6)); - - if (param_getchar(Cmd, 0)=='*') { - blockNo = 3; - SectorsCnt = NumOfSectors( param_getchar(Cmd+1, 0) ); - } else { - blockNo = param_get8(Cmd, 0); - } - - ctmp = tolower(param_getchar(Cmd, 1)); - clen = param_getlength(Cmd, 1); - if (clen == 1) { - switch (ctmp) { - case 'a': - keyType = 0; - break; - case 'b': - keyType = 1; - break; - case '?': - keyType = 2; - break; - default: - PrintAndLogEx(FAILED, "Key type must be A , B or ?"); - free(keyBlock); - return 1; - }; - } - - for (i = 2; param_getchar(Cmd, i); i++) { - - ctmp = tolower(param_getchar(Cmd, i)); - clen = param_getlength(Cmd, i); - - if (clen == 12) { - - if ( param_gethex(Cmd, i, keyBlock + 6 * keycnt, 12) ){ - PrintAndLogEx(FAILED, "not hex, skipping"); - continue; - } - - if ( keyitems - keycnt < 2) { - p = realloc(keyBlock, 6 * (keyitems += 64)); - if (!p) { - PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); - free(keyBlock); - return 2; - } - keyBlock = p; - } - PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex( (keyBlock + 6*keycnt), 6 ) );; - keycnt++; - } else if ( clen == 1 ) { - if (ctmp == 't' ) { transferToEml = 1; continue; } - if (ctmp == 'd' ) { createDumpFile = 1; continue; } - } else { - // May be a dic file - if ( param_getstr(Cmd, i, filename, sizeof(filename)) >= FILE_PATH_SIZE ) { - PrintAndLogEx(FAILED, "File name too long"); - continue; - } - - f = fopen( filename , "r"); - if ( !f ) { - PrintAndLogEx(FAILED, "File: " _YELLOW_(%s) ": not found or locked.", filename); - continue; - } - - // load keys from dictionary file - while( fgets(buf, sizeof(buf), f) ){ - if (strlen(buf) < 12 || buf[11] == '\n') - continue; - - while (fgetc(f) != '\n' && !feof(f)) ; //goto next line - - if( buf[0]=='#' ) continue; //The line start with # is comment, skip - - // codesmell, only checks first char? - if (!isxdigit(buf[0])){ - PrintAndLogEx(FAILED, "File content error. '" _YELLOW_(%s)"' must include 12 HEX symbols",buf); - continue; - } - - buf[12] = 0; - - if ( keyitems - keycnt < 2) { - p = realloc(keyBlock, 6 * (keyitems += 64)); - if (!p) { - PrintAndLogEx(FAILED, "Cannot allocate memory for defKeys"); - free(keyBlock); - fclose(f); - return 2; - } - keyBlock = p; - } - memset(keyBlock + 6 * keycnt, 0, 6); - num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + 6*keycnt); - //PrintAndLogEx(NORMAL, "check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + 6*keycnt, 6)); - keycnt++; - memset(buf, 0, sizeof(buf)); - } - fclose(f); - PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_(%s), keycnt, filename); - } - } - - if (keycnt == 0) { - PrintAndLogEx(INFO, "No key specified, trying default keys"); - for (;keycnt < MIFARE_DEFAULTKEYS_SIZE; keycnt++) - PrintAndLogEx(NORMAL, "[%2d] %02x%02x%02x%02x%02x%02x", keycnt, - (keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], - (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); - } - - // initialize storage for found keys - e_sector = calloc(SectorsCnt, sizeof(sector_t)); - if (e_sector == NULL) { - free(keyBlock); - return 1; - } - - // empty e_sector - for(int i = 0; i < SectorsCnt; ++i){ - e_sector[i].Key[0] = 0xffffffffffff; - e_sector[i].Key[1] = 0xffffffffffff; - e_sector[i].foundKey[0] = false; - e_sector[i].foundKey[1] = false; - } - - - uint8_t trgKeyType = 0; - uint32_t max_keys = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; - - // time - uint64_t t1 = msclock(); - - - // check keys. - for (trgKeyType = (keyType==2)?0:keyType; trgKeyType < 2; (keyType==2) ? (++trgKeyType) : (trgKeyType=2) ) { - - int b = blockNo; - for (int i = 0; i < SectorsCnt; ++i) { - - // skip already found keys. - if (e_sector[i].foundKey[trgKeyType]) continue; - - for (uint32_t c = 0; c < keycnt; c += max_keys) { - - printf("."); fflush(stdout); - if (ukbhit()) { - int gc = getchar(); (void)gc; - PrintAndLogEx(INFO, "\naborted via keyboard!\n"); - goto out; - } - - uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; - - res = mfCheckKeys(b, trgKeyType, true, size, &keyBlock[6*c], &key64); - if (!res) { - e_sector[i].Key[trgKeyType] = key64; - e_sector[i].foundKey[trgKeyType] = true; - break; - } - - - } - b < 127 ? ( b +=4 ) : ( b += 16 ); - } - } - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "\nTime in checkkeys: %.0f seconds\n", (float)t1/1000.0); - - - // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? - if ( keyType != 1 ) { - PrintAndLogEx(INFO, "testing to read key B..."); - for (i = 0; i < SectorsCnt; i++) { - // KEY A but not KEY B - if ( e_sector[i].foundKey[0] && !e_sector[i].foundKey[1] ) { - - uint8_t sectrail = (FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); - - PrintAndLogEx(NORMAL, "Reading block %d", sectrail); - - UsbCommand c = {CMD_MIFARE_READBL, {sectrail, 0, 0}}; - num_to_bytes(e_sector[i].Key[0], 6, c.d.asBytes); // KEY A - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - if ( !WaitForResponseTimeout(CMD_ACK,&resp,1500)) continue; - - uint8_t isOK = resp.arg[0] & 0xff; - if (!isOK) continue; - - uint8_t *data = resp.d.asBytes; - key64 = bytes_to_num(data+10, 6); - if (key64) { - PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data+10, 6)); - e_sector[i].foundKey[1] = 1; - e_sector[i].Key[1] = key64; - } - } - } - } - -out: - - //print keys - printKeyTable( SectorsCnt, e_sector ); - - if (transferToEml) { - uint8_t block[16] = {0x00}; - for (uint8_t i = 0; i < SectorsCnt; ++i ) { - mfEmlGetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); - if (e_sector[i].foundKey[0]) - num_to_bytes(e_sector[i].Key[0], 6, block); - if (e_sector[i].foundKey[1]) - num_to_bytes(e_sector[i].Key[1], 6, block+10); - mfEmlSetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); - } - PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); - } - - if (createDumpFile) { - fptr = GenerateFilename("hf-mf-", "-key.bin"); - if (fptr == NULL) { - free(keyBlock); - free(e_sector); - return 1; - } - - FILE *fkeys = fopen(fptr, "wb"); - if (fkeys == NULL) { - PrintAndLogEx(WARNING, "Could not create file " _YELLOW_(%s), fptr); - free(keyBlock); - free(e_sector); - return 1; - } - PrintAndLogEx(INFO, "Printing keys to binary file " _YELLOW_(%s)"...", fptr); - - for( i=0; i>1) - , exitAfterNReads - , flags - , flags); - - UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {flags, exitAfterNReads, 0}}; - memcpy(c.d.asBytes, uid, sizeof(uid)); - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - - if(flags & FLAG_INTERACTIVE) { - PrintAndLogEx(INFO, "Press pm3-button or send another cmd to abort simulation"); - - while( !ukbhit() ){ - if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500) ) continue; - if ( !(flags & FLAG_NR_AR_ATTACK) ) break; - if ( (resp.arg[0] & 0xffff) != CMD_SIMULATE_MIFARE_CARD ) break; - - memcpy(data, resp.d.asBytes, sizeof(data)); - readerAttack(data[0], setEmulatorMem, verbose); - } - showSectorTable(); - } - return 0; -} - -int CmdHF14AMfSniff(const char *Cmd){ - bool wantLogToFile = false; - bool wantDecrypt = false; - //bool wantSaveToEml = false; TODO - bool wantSaveToEmlFile = false; - - //var - int res = 0, len = 0, blockLen = 0; - int pckNum = 0, num = 0; - uint8_t sak = 0; - uint8_t uid[10]; - uint8_t uid_len = 0; - uint8_t atqa[2] = {0x00, 0x00}; - bool isTag = false; - uint8_t *buf = NULL; - uint16_t bufsize = 0; - uint8_t *bufPtr = NULL; - uint16_t traceLen = 0; - - memset(uid, 0x00, sizeof(uid)); - - char ctmp = tolower(param_getchar(Cmd, 0)); - if ( ctmp == 'h') return usage_hf14_sniff(); - - for (int i = 0; i < 4; i++) { - ctmp = tolower(param_getchar(Cmd, i)); - if (ctmp == 'l') wantLogToFile = true; - if (ctmp == 'd') wantDecrypt = true; - //if (ctmp == 'e') wantSaveToEml = true; TODO - if (ctmp == 'f') wantSaveToEmlFile = true; - } - - PrintAndLogEx(NORMAL, "-------------------------------------------------------------------------\n"); - PrintAndLogEx(NORMAL, "Executing mifare sniffing command. \n"); - PrintAndLogEx(NORMAL, "Press the key on the proxmark3 device to abort both proxmark3 and client.\n"); - PrintAndLogEx(NORMAL, "Press the key on pc keyboard to abort the client.\n"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------------------\n"); - - UsbCommand c = {CMD_MIFARE_SNIFFER, {0, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - - // wait cycle - while (true) { - printf("."); fflush(stdout); - if (ukbhit()) { - int gc = getchar(); (void)gc; - PrintAndLogEx(INFO, "\naborted via keyboard!\n"); - break; - } - - if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { - continue; - } - - res = resp.arg[0] & 0xff; - traceLen = resp.arg[1]; - len = resp.arg[2]; - - if (res == 0) { - PrintAndLogEx(SUCCESS, "hf mifare sniff finished"); - free(buf); - return 0; - } - - if (res == 1) { // there is (more) data to be transferred - if (pckNum == 0) { // first packet, (re)allocate necessary buffer - if (traceLen > bufsize || buf == NULL) { - uint8_t *p; - if (buf == NULL) // not yet allocated - p = calloc(traceLen, sizeof(uint8_t)); - else // need more memory - p = realloc(buf, traceLen); - - if (p == NULL) { - PrintAndLogEx(FAILED, "Cannot allocate memory for trace"); - free(buf); - return 2; - } - buf = p; - } - bufPtr = buf; - bufsize = traceLen; - memset(buf, 0x00, traceLen); - } - - // what happens if LEN is bigger then TRACELEN --iceman - memcpy(bufPtr, resp.d.asBytes, len); - bufPtr += len; - pckNum++; - } - - if (res == 2) { // received all data, start displaying - blockLen = bufPtr - buf; - bufPtr = buf; - PrintAndLogEx(NORMAL, ">\n"); - PrintAndLogEx(SUCCESS, "received trace len: %d packages: %d", blockLen, pckNum); - while (bufPtr - buf < blockLen) { - bufPtr += 6; // skip (void) timing information - len = *((uint16_t *)bufPtr); - if(len & 0x8000) { - isTag = true; - len &= 0x7fff; - } else { - isTag = false; - } - bufPtr += 2; - - // the uid identification package - // 0xFF 0xFF xx xx xx xx xx xx xx xx xx xx aa aa cc 0xFF 0xFF - // x = uid, a = atqa, c = sak - if ((len == 17) && (bufPtr[0] == 0xff) && (bufPtr[1] == 0xff) && (bufPtr[15] == 0xff) && (bufPtr[16] == 0xff)) { - memcpy(uid, bufPtr + 2, 10); - memcpy(atqa, bufPtr + 2 + 10, 2); - switch (atqa[0] & 0xC0) { - case 0x80: uid_len = 10; break; - case 0x40: uid_len = 7; break; - default: uid_len = 4; break; - } - sak = bufPtr[14]; - PrintAndLogEx(SUCCESS, "UID %s | ATQA %02x %02x | SAK 0x%02x", - sprint_hex(uid, uid_len), - atqa[1], - atqa[0], - sak); - if (wantLogToFile || wantDecrypt) { - FillFileNameByUID(logHexFileName, uid, ".log", uid_len); - AddLogCurrentDT(logHexFileName); - PrintAndLogEx(SUCCESS, "Trace saved to %s", logHexFileName); - } - if (wantDecrypt) - mfTraceInit(uid, uid_len, atqa, sak, wantSaveToEmlFile); - } else { - PrintAndLogEx(NORMAL, "%03d| %s |%s", num, isTag ? "TAG" : "RDR", sprint_hex(bufPtr, len)); - if (wantLogToFile) - AddLogHex(logHexFileName, isTag ? "TAG| ":"RDR| ", bufPtr, len); - if (wantDecrypt) - mfTraceDecode(bufPtr, len, wantSaveToEmlFile); - num++; - } - bufPtr += len; - bufPtr += ((len-1)/8+1); // ignore parity - } - pckNum = 0; - } - } // while (true) - - free(buf); - return 0; -} - -int CmdHF14AMfDbg(const char *Cmd) { - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h' ) return usage_hf14_dbg(); - - uint8_t dbgMode = param_get8ex(Cmd, 0, 0, 10); - if (dbgMode > 4) return usage_hf14_dbg(); - - UsbCommand c = {CMD_MIFARE_SET_DBGMODE, {dbgMode, 0, 0}}; - SendCommand(&c); - return 0; -} - -int CmdHF14AMfKeyBrute(const char *Cmd) { - - uint8_t blockNo = 0, keytype = 0; - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint64_t foundkey = 0; - - char cmdp = tolower(param_getchar(Cmd, 0)); - if ( cmdp == 'h' ) return usage_hf14_keybrute(); - - // block number - blockNo = param_get8(Cmd, 0); - - // keytype - cmdp = tolower(param_getchar(Cmd, 1)); - if ( cmdp == 'b' ) keytype = 1; - - // key - if (param_gethex(Cmd, 2, key, 12)) return usage_hf14_keybrute(); - - uint64_t t1 = msclock(); - - if (mfKeyBrute( blockNo, keytype, key, &foundkey)) - PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey); - else - PrintAndLogEx(FAILED, "key not found"); - - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "\ntime in keybrute: %.0f seconds\n", (float)t1/1000.0); - return 0; -} - -void printKeyTable( uint8_t sectorscnt, sector_t *e_sector ){ - char strA[12+1] = {0}; - char strB[12+1] = {0}; - PrintAndLogEx(NORMAL, "|---|----------------|---|----------------|---|"); - PrintAndLogEx(NORMAL, "|sec|key A |res|key B |res|"); - PrintAndLogEx(NORMAL, "|---|----------------|---|----------------|---|"); - for (uint8_t i = 0; i < sectorscnt; ++i) { - - snprintf(strA, sizeof(strA), "------------"); - snprintf(strB, sizeof(strB), "------------"); - - if ( e_sector[i].foundKey[0] ) - snprintf(strA, sizeof(strA), "%012" PRIx64, e_sector[i].Key[0]); - - if ( e_sector[i].foundKey[1] ) - snprintf(strB, sizeof(strB), "%012" PRIx64, e_sector[i].Key[1]); - - - PrintAndLogEx(NORMAL, "|%03d| %s | %d | %s | %d |" - , i - , strA, e_sector[i].foundKey[0] - , strB, e_sector[i].foundKey[1] - ); - } - PrintAndLogEx(NORMAL, "|---|----------------|---|----------------|---|"); -} - -// EMULATOR COMMANDS -int CmdHF14AMfEGet(const char *Cmd) { - uint8_t blockNo = 0; - uint8_t data[16] = {0x00}; - char c = tolower(param_getchar(Cmd, 0)); - - if (strlen(Cmd) < 1 || c == 'h') return usage_hf14_eget(); - - blockNo = param_get8(Cmd, 0); - - PrintAndLogEx(NORMAL, ""); - if (!mfEmlGetMem(data, blockNo, 1)) { - PrintAndLogEx(NORMAL, "data[%3d]:%s", blockNo, sprint_hex(data, sizeof(data))); - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - return 0; -} - -int CmdHF14AMfEClear(const char *Cmd) { - char c = tolower(param_getchar(Cmd, 0)); - if (c == 'h') return usage_hf14_eclr(); - - UsbCommand cmd = {CMD_MIFARE_EML_MEMCLR, {0, 0, 0}}; - clearCommandBuffer(); - SendCommand(&cmd); - return 0; -} - -int CmdHF14AMfESet(const char *Cmd) { - char c = tolower(param_getchar(Cmd, 0)); - uint8_t memBlock[16]; - uint8_t blockNo = 0; - memset(memBlock, 0x00, sizeof(memBlock)); - - if (strlen(Cmd) < 3 || c == 'h') - return usage_hf14_eset(); - - blockNo = param_get8(Cmd, 0); - - if (param_gethex(Cmd, 1, memBlock, 32)) { - PrintAndLogEx(WARNING, "block data must include 32 HEX symbols"); - return 1; - } - - // 1 - blocks count - return mfEmlSetMem(memBlock, blockNo, 1); -} - -int CmdHF14AMfELoad(const char *Cmd) { - - size_t counter = 0; - char filename[FILE_PATH_SIZE]; - int blockNum, numBlocks, nameParamNo = 1; - uint8_t blockWidth = 16; - char c = tolower(param_getchar(Cmd, 0)); - - if ( strlen(Cmd) < 2 && c == 'h' ) - return usage_hf14_eload(); - - switch (c) { - case '0' : numBlocks = MIFARE_MINI_MAXBLOCK; break; - case '1' : - case '\0': numBlocks = MIFARE_1K_MAXBLOCK; break; - case '2' : numBlocks = MIFARE_2K_MAXBLOCK; break; - case '4' : numBlocks = MIFARE_4K_MAXBLOCK; break; - case 'u' : numBlocks = 255; blockWidth = 4; break; - default: { - numBlocks = MIFARE_1K_MAXBLOCK; - nameParamNo = 0; - } - } - uint32_t numblk2 = param_get32ex(Cmd, 2, 0, 10); - if (numblk2 > 0) - numBlocks = numblk2; - - param_getstr(Cmd, nameParamNo, filename, sizeof(filename)); - - uint8_t *data = calloc(4096, sizeof(uint8_t)); - size_t datalen = 0; - //int res = loadFile(filename, "bin", data, &datalen); - int res = loadFileEML( filename, "eml", data, &datalen); - if ( res ) { - free(data); - return 1; - } - - // 64 or 256 blocks. - if ( (datalen % blockWidth) != 0 ) { - PrintAndLogEx(FAILED, "File content error. Size doesn't match blockwidth "); - free(data); - return 2; - } - - PrintAndLogEx(INFO, "Copying to emulator memory"); - - blockNum = 0; - while ( datalen ) { - - if (mfEmlSetMem_xt(data + counter, blockNum, 1, blockWidth)) { - PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockNum); - free(data); - return 3; - } - printf("."); fflush(stdout); - - blockNum++; - counter += blockWidth; - datalen -= blockWidth; - } - PrintAndLogEx(NORMAL, "\n"); - - // Ultralight /Ntag - if ( blockWidth == 4 ) { - if ((blockNum != numBlocks)) { - PrintAndLogEx(FAILED, "Warning, Ultralight/Ntag file content, Loaded %d blocks into emulator memory", blockNum); - free(data); - return 0; - } - } else { - if ((blockNum != numBlocks)) { - PrintAndLogEx(FAILED, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", blockNum, numBlocks); - free(data); - return 4; - } - } - PrintAndLogEx(SUCCESS, "Loaded %d blocks from file: " _YELLOW_(%s), blockNum, filename); - free(data); - return 0; -} - -int CmdHF14AMfESave(const char *Cmd) { - - char filename[FILE_PATH_SIZE]; - char * fnameptr = filename; - uint8_t *dump; - int len, bytes, nameParamNo = 1; - uint16_t blocks; - - memset(filename, 0, sizeof(filename)); - - char c = tolower(param_getchar(Cmd, 0)); - if (c == 'h') return usage_hf14_esave(); - - blocks = NumOfBlocks(c); - bytes = blocks * MFBLOCK_SIZE; - - dump = calloc(bytes, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return 1; - } - memset(dump, 0, bytes); - - PrintAndLogEx(INFO, "downloading from emulator memory"); - if (!GetFromDevice( BIG_BUF_EML, dump, bytes, 0, NULL, 2500, false)) { - PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); - free(dump); - return 2; - } - - len = param_getstr(Cmd, nameParamNo, filename, sizeof(filename)); - if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; - - // user supplied filename? - if (len < 1) { - fnameptr += sprintf(fnameptr, "hf-mf-"); - FillFileNameByUID(fnameptr, dump, "-dump", 4); - } - - saveFile(filename, "bin", dump, bytes); - saveFileEML(filename, "eml", dump, bytes, MFBLOCK_SIZE); - saveFileJSON(filename, "json", jsfCardMemory, dump, bytes); - free(dump); - return 0; -} - -int CmdHF14AMfECFill(const char *Cmd) { - uint8_t keyType = 0; - uint8_t numSectors = 16; - char c = tolower(param_getchar(Cmd, 0)); - - if (strlen(Cmd) < 1 || c == 'h') - return usage_hf14_ecfill(); - - if (c != 'a' && c != 'b') { - PrintAndLogEx(WARNING, "Key type must be A or B"); - return 1; - } - if (c != 'a') - keyType = 1; - - c = tolower(param_getchar(Cmd, 1)); - numSectors = NumOfSectors(c); - - PrintAndLogEx(NORMAL, "--params: numSectors: %d, keyType: %c\n", numSectors, (keyType == 0) ? 'A' : 'B'); - UsbCommand cmd = {CMD_MIFARE_EML_CARDLOAD, {numSectors, keyType, 0}}; - clearCommandBuffer(); - SendCommand(&cmd); - return 0; -} - -int CmdHF14AMfEKeyPrn(const char *Cmd) { - int i; - uint8_t numSectors; - uint8_t data[16]; - uint64_t keyA, keyB; - - char c = tolower(param_getchar(Cmd, 0)); - if ( c == 'h' ) - return usage_hf14_ekeyprn(); - - numSectors = NumOfSectors(c); - - PrintAndLogEx(NORMAL, "|---|----------------|----------------|"); - PrintAndLogEx(NORMAL, "|sec|key A |key B |"); - PrintAndLogEx(NORMAL, "|---|----------------|----------------|"); - for (i = 0; i < numSectors; i++) { - if (mfEmlGetMem(data, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1)) { - PrintAndLogEx(WARNING, "error get block %d", FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); - break; - } - keyA = bytes_to_num(data, 6); - keyB = bytes_to_num(data + 10, 6); - PrintAndLogEx(NORMAL, "|%03d| %012" PRIx64 " | %012" PRIx64 " |", i, keyA, keyB); - } - PrintAndLogEx(NORMAL, "|---|----------------|----------------|"); - return 0; -} - -// CHINESE MAGIC COMMANDS -int CmdHF14AMfCSetUID(const char *Cmd) { - uint8_t wipeCard = 0; - uint8_t uid[8] = {0x00}; - uint8_t oldUid[8] = {0x00}; - uint8_t atqa[2] = {0x00}; - uint8_t sak[1] = {0x00}; - uint8_t atqaPresent = 1; - int res, argi = 0; - char ctmp; - - if (strlen(Cmd) < 1 || param_getchar(Cmd, argi) == 'h') - return usage_hf14_csetuid(); - - if (param_getchar(Cmd, argi) && param_gethex(Cmd, argi, uid, 8)) - return usage_hf14_csetuid(); - - argi++; - - ctmp = tolower(param_getchar(Cmd, argi)); - if (ctmp == 'w') { - wipeCard = 1; - atqaPresent = 0; - } - - if (atqaPresent) { - if (param_getchar(Cmd, argi)) { - if (param_gethex(Cmd, argi, atqa, 4)) { - PrintAndLogEx(WARNING, "ATQA must include 4 HEX symbols"); - return 1; - } - argi++; - if (!param_getchar(Cmd, argi) || param_gethex(Cmd, argi, sak, 2)) { - PrintAndLogEx(WARNING, "SAK must include 2 HEX symbols"); - return 1; - } - argi++; - } else - atqaPresent = 0; - } - - if (!wipeCard) { - ctmp = tolower(param_getchar(Cmd, argi)); - if (ctmp == 'w') { - wipeCard = 1; - } - } - - PrintAndLogEx(NORMAL, "--wipe card:%s uid:%s", (wipeCard)?"YES":"NO", sprint_hex(uid, 4)); - - res = mfCSetUID(uid, (atqaPresent) ? atqa : NULL, (atqaPresent) ? sak : NULL, oldUid, wipeCard); - if (res) { - PrintAndLogEx(WARNING, "Can't set UID. error=%d", res); - return 1; - } - - PrintAndLogEx(SUCCESS, "old UID:%s", sprint_hex(oldUid, 4)); - PrintAndLogEx(SUCCESS, "new UID:%s", sprint_hex(uid, 4)); - return 0; -} - -int CmdHF14AMfCSetBlk(const char *Cmd) { - uint8_t block[16] = {0x00}; - uint8_t blockNo = 0; - uint8_t params = MAGIC_SINGLE; - int res; - char ctmp = tolower(param_getchar(Cmd, 0)); - - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_csetblk(); - - blockNo = param_get8(Cmd, 0); - - if (param_gethex(Cmd, 1, block, 32)) return usage_hf14_csetblk(); - - ctmp = tolower(param_getchar(Cmd, 2)); - if (ctmp == 'w') - params |= MAGIC_WIPE; - - PrintAndLogEx(NORMAL, "--block number:%2d data:%s", blockNo, sprint_hex(block, 16)); - - res = mfCSetBlock(blockNo, block, NULL, params); - if (res) { - PrintAndLogEx(WARNING, "Can't write block. error=%d", res); - return 1; - } - return 0; -} - -int CmdHF14AMfCLoad(const char *Cmd) { - - uint8_t buf8[16] = {0x00}; - uint8_t fillFromEmulator = 0; - int blockNum, flags = 0; - bool fillFromJson = false; - bool fillFromBin = false; - char fileName[50] = {0}; - - char ctmp = tolower(param_getchar(Cmd, 0)); - if ( param_getlength(Cmd, 0) == 1 ) { - if (ctmp == 'h' || ctmp == 0x00) return usage_hf14_cload(); - if (ctmp == 'e' ) fillFromEmulator = 1; - if (ctmp == 'j' ) fillFromJson = true; - if (ctmp == 'b' ) fillFromBin = true; - } - - if (fillFromJson || fillFromBin) - param_getstr(Cmd, 1, fileName, sizeof(fileName)); - - - if (fillFromEmulator) { - for (blockNum = 0; blockNum < 16 * 4; blockNum += 1) { - if (mfEmlGetMem(buf8, blockNum, 1)) { - PrintAndLogEx(WARNING, "Cant get block: %d", blockNum); - return 2; - } - if (blockNum == 0) flags = MAGIC_INIT + MAGIC_WUPC; // switch on field and send magic sequence - if (blockNum == 1) flags = 0; // just write - if (blockNum == 16 * 4 - 1) flags = MAGIC_HALT + MAGIC_OFF; // Done. Magic Halt and switch off field. - - if (mfCSetBlock(blockNum, buf8, NULL, flags)) { - PrintAndLogEx(WARNING, "Cant set magic card block: %d", blockNum); - return 3; - } - printf("."); fflush(stdout); - } - PrintAndLogEx(NORMAL, "\n"); - return 0; - } - - size_t maxdatalen = 4096; - uint8_t *data = calloc(maxdatalen, sizeof(uint8_t)); - size_t datalen = 0; - int res = 0; - if (fillFromBin) { - res = loadFile(fileName, "bin", data, &datalen); - } else { - if (fillFromJson) { - res = loadFileJSON(fileName, "json", data, maxdatalen, &datalen); - } else { - res = loadFileEML( Cmd, "eml", data, &datalen); - } - } - - if ( res ) { - if ( data ) - free(data); - return 1; - } - -// PrintAndLogEx(INFO, "DATA | %s", sprint_hex(data+1000, 24) ); - - - // 64 or 256blocks. - if (datalen != 1024 && datalen != 4096) { - PrintAndLogEx(WARNING, "File content error. "); - free(data); - return 2; - } - - PrintAndLogEx(INFO, "Copying to magic card"); - - blockNum = 0; - while ( datalen ) { - - // switch on field and send magic sequence - if (blockNum == 0) flags = MAGIC_INIT + MAGIC_WUPC; - - // write - if (blockNum == 1) flags = 0; - - // Switch off field. - if (blockNum == 16 * 4 - 1) flags = MAGIC_HALT + MAGIC_OFF; - - if (mfCSetBlock(blockNum, data + (16 * blockNum), NULL, flags)) { - PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockNum); - free(data); - return 3; - } - - datalen -= 16; - - printf("."); fflush(stdout); - blockNum++; - - // magic card type - mifare 1K - if (blockNum >= MIFARE_1K_MAXBLOCK ) break; - } - PrintAndLogEx(NORMAL, "\n"); - - // 64 or 256blocks. - if (blockNum != 16 * 4 && blockNum != 32 * 4 + 8 * 16){ - PrintAndLogEx(WARNING, "File content error. There must be 64 blocks"); - free(data); - return 4; - } - - PrintAndLogEx(SUCCESS, "Card loaded %d blocks from file", blockNum); - free(data); - return 0; -} - -int CmdHF14AMfCGetBlk(const char *Cmd) { - uint8_t data[16] = {0}; - uint8_t blockNo = 0; - int res; - memset(data, 0x00, sizeof(data)); - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_cgetblk(); - - blockNo = param_get8(Cmd, 0); - - PrintAndLogEx(NORMAL, "--block number:%2d ", blockNo); - - res = mfCGetBlock(blockNo, data, MAGIC_SINGLE); - if (res) { - PrintAndLogEx(WARNING, "Can't read block. error=%d", res); - return 1; - } - - PrintAndLogEx(NORMAL, "data: %s", sprint_hex(data, sizeof(data))); - - if (mfIsSectorTrailer(blockNo)) { - PrintAndLogEx(NORMAL, "Trailer decoded:"); - PrintAndLogEx(NORMAL, "Key A: %s", sprint_hex_inrow(data, 6)); - PrintAndLogEx(NORMAL, "Key B: %s", sprint_hex_inrow(&data[10], 6)); - int bln = mfFirstBlockOfSector(mfSectorNum(blockNo)); - int blinc = (mfNumBlocksPerSector(mfSectorNum(blockNo)) > 4) ? 5 : 1; - for (int i = 0; i < 4; i++) { - PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &data[6])); - bln += blinc; - } - PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&data[9], 1)); - } - - return 0; -} - -int CmdHF14AMfCGetSc(const char *Cmd) { - uint8_t data[16] = {0}; - uint8_t sector = 0; - int i, res, flags; - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_cgetsc(); - - sector = param_get8(Cmd, 0); - if (sector > 39) { - PrintAndLogEx(WARNING, "Sector number must be less then 40"); - return 1; - } - - PrintAndLogEx(NORMAL, "\n # | data | Sector | %02d/ 0x%02X ", sector, sector); - PrintAndLogEx(NORMAL, "----+------------------------------------------------"); - uint8_t blocks = 4; - uint8_t start = sector * 4; - if ( sector > 32 ) { - blocks = 16; - start = 128 + ( sector - 32 ) * 16; - } - - flags = MAGIC_INIT + MAGIC_WUPC; - - for (i = 0; i < blocks; i++) { - if (i == 1) flags = 0; - if (i == blocks-1) flags = MAGIC_HALT + MAGIC_OFF; - - res = mfCGetBlock( start + i, data, flags); - if (res) { - PrintAndLogEx(WARNING, "Can't read block. %d error=%d", start + i, res); - return 1; - } - PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data, 16)); - } - return 0; -} - -int CmdHF14AMfCSave(const char *Cmd) { - - char filename[FILE_PATH_SIZE]; - char * fnameptr = filename; - uint8_t *dump; - bool fillEmulator = false; - bool errors = false, hasname = false, useuid = false; - int i, len, flags; - uint8_t numblocks = 0, cmdp = 0; - uint16_t bytes = 0; - char ctmp; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - ctmp = tolower(param_getchar(Cmd, cmdp)); - switch (ctmp) { - case 'e': - useuid = true; - fillEmulator = true; - cmdp++; - break; - case 'h': - return usage_hf14_csave(); - case '0': - case '1': - case '2': - case '4': - numblocks = NumOfBlocks(ctmp); - bytes = numblocks * MFBLOCK_SIZE; - PrintAndLogEx(SUCCESS, "Saving magic mifare %cK", ctmp); - cmdp++; - break; - case 'u': - useuid = true; - hasname = true; - cmdp++; - break; - case 'o': - len = param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE); - if (len < 1) { - errors = true; - break; - } - - useuid = false; - hasname = true; - cmdp += 2; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - - if (!hasname && !fillEmulator) errors = true; - - if (errors || cmdp == 0) return usage_hf14_csave(); - - dump = calloc(bytes, sizeof(uint8_t)); - if (!dump) { - PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); - return 1; - } - memset(dump, 0, bytes); - - flags = MAGIC_INIT + MAGIC_WUPC; - for (i = 0; i < numblocks; i++) { - if (i == 1) flags = 0; - if (i == numblocks - 1) flags = MAGIC_HALT + MAGIC_OFF; - - if (mfCGetBlock(i, dump + (i*MFBLOCK_SIZE), flags)) { - PrintAndLogEx(WARNING, "Cant get block: %d", i); - free(dump); - return 2; - } - } - - if ( useuid ){ - fnameptr += sprintf(fnameptr, "hf-mf-"); - FillFileNameByUID(fnameptr, dump, "-dump", 4); - } - - if (fillEmulator) { - PrintAndLogEx(INFO, "uploading to emulator memory"); - for (i = 0; i < numblocks; i += 5) { - if (mfEmlSetMem(dump + (i*MFBLOCK_SIZE), i, 5)) { - PrintAndLogEx(WARNING, "Cant set emul block: %d", i); - } - printf("."); fflush(stdout); - } - PrintAndLogEx(NORMAL, "\n"); - PrintAndLogEx(SUCCESS, "uploaded %d bytes to emulator memory", bytes); - } - - saveFile(filename, "bin", dump, bytes); - saveFileEML(filename, "eml", dump, bytes, MFBLOCK_SIZE); - saveFileJSON(filename, "json", jsfCardMemory, dump, bytes); - free(dump); - return 0; -} - -//needs nt, ar, at, Data to decrypt -int CmdHf14AMfDecryptBytes(const char *Cmd){ - - char ctmp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_decryptbytes(); - - uint32_t nt = param_get32ex(Cmd,0,0,16); - uint32_t ar_enc = param_get32ex(Cmd,1,0,16); - uint32_t at_enc = param_get32ex(Cmd,2,0,16); - - int len = param_getlength(Cmd, 3); - if (len & 1 ) { - PrintAndLogEx(WARNING, "Uneven hex string length. LEN=%d", len); - return 1; - } - - PrintAndLogEx(NORMAL, "nt\t%08X", nt); - PrintAndLogEx(NORMAL, "ar enc\t%08X", ar_enc); - PrintAndLogEx(NORMAL, "at enc\t%08X", at_enc); - - uint8_t *data = calloc(len, sizeof(uint8_t)); - param_gethex_ex(Cmd, 3, data, &len); - len >>= 1; - tryDecryptWord( nt, ar_enc, at_enc, data, len); - free (data); - return 0; -} - -int CmdHf14AMfSetMod(const char *Cmd) { - uint8_t key[6] = {0, 0, 0, 0, 0, 0}; - uint8_t mod = 2; - - char ctmp = param_getchar(Cmd, 0); - if (ctmp == '0') { - mod = 0; - } else if (ctmp == '1') { - mod = 1; - } - int gethexfail = param_gethex(Cmd, 1, key, 12); - if (mod == 2 || gethexfail) { - PrintAndLogEx(NORMAL, "Sets the load modulation strength of a MIFARE Classic EV1 card."); - PrintAndLogEx(NORMAL, "Usage: hf mf setmod <0|1> "); - PrintAndLogEx(NORMAL, " 0 = normal modulation"); - PrintAndLogEx(NORMAL, " 1 = strong modulation (default)"); - return 1; - } - - UsbCommand c = {CMD_MIFARE_SETMOD, {mod, 0, 0}}; - memcpy(c.d.asBytes, key, 6); - clearCommandBuffer(); - SendCommand(&c); - - UsbCommand resp; - if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { - uint8_t ok = resp.arg[0] & 0xff; - PrintAndLogEx(SUCCESS, "isOk:%02x", ok); - if (!ok) - PrintAndLogEx(FAILED, "Failed."); - } else { - PrintAndLogEx(WARNING, "Command execute timeout"); - } - return 0; -} - -// Mifare NACK bug detection -int CmdHf14AMfNack(const char *Cmd) { - - bool verbose = false; - char ctmp = tolower(param_getchar(Cmd, 0)); - if ( ctmp == 'h' ) return usage_hf14_nack(); - if ( ctmp == 'v' ) verbose = true; - - if ( verbose ) - PrintAndLogEx(INFO, "Started testing card for NACK bug. Press key to abort"); - - detect_classic_nackbug(verbose); - return 0; -} - -int CmdHF14AMfice(const char *Cmd) { - - uint8_t blockNo = 0; - uint8_t keyType = 0; - uint8_t trgBlockNo = 0; - uint8_t trgKeyType = 1; - bool slow = false; - bool initialize = true; - bool acquisition_completed = false; - uint8_t cmdp=0; - uint32_t flags = 0; - uint32_t total_num_nonces = 0; - char ctmp; - char filename[FILE_PATH_SIZE], *fptr; - FILE *fnonces = NULL; - UsbCommand resp; - - uint32_t part_limit = 3000; - uint32_t limit = 50000; - - while ((ctmp = param_getchar(Cmd, cmdp))) { - switch(tolower(ctmp)) - { - case 'h': - return usage_hf14_ice(); - case 'f': - param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE); - cmdp++; - break; - case 'l': - limit = param_get32ex(Cmd, cmdp+1, 50000, 10); - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", ctmp); - usage_hf14_ice(); - return 1; - } - cmdp++; - } - - if(filename[0]=='\0') - { - fptr = GenerateFilename("hf-mf-","-nonces.bin"); - if (fptr == NULL) - return 1; - strcpy(filename, fptr); - } - - PrintAndLogEx(NORMAL, "Collecting %u nonces \n", limit); - - if ((fnonces = fopen(filename,"wb")) == NULL) { - PrintAndLogEx(WARNING, "Could not create file " _YELLOW_(%s),filename); - return 3; - } - - clearCommandBuffer(); - - uint64_t t1 = msclock(); - - do { - if (ukbhit()) { - int gc = getchar(); (void)gc; - PrintAndLogEx(INFO, "\naborted via keyboard!\n"); - break; - } - - flags = 0; - flags |= initialize ? 0x0001 : 0; - flags |= slow ? 0x0002 : 0; - UsbCommand c = {CMD_MIFARE_ACQUIRE_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; - clearCommandBuffer(); - SendCommand(&c); - - if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) goto out; - if (resp.arg[0]) goto out; - - uint32_t items = resp.arg[2]; - if (fnonces) { - fwrite(resp.d.asBytes, 1, items*4, fnonces); - fflush(fnonces); - } - - total_num_nonces += items; - if ( total_num_nonces > part_limit ) { - PrintAndLogEx(INFO, "Total nonces %u\n", total_num_nonces); - part_limit += 3000; - } - - acquisition_completed = ( total_num_nonces > limit); - - initialize = false; - - } while (!acquisition_completed); - -out: - PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock()-t1)/1000); - - if ( fnonces ) { - fflush(fnonces); - fclose(fnonces); - } - - UsbCommand c = {CMD_MIFARE_ACQUIRE_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, 4}}; - clearCommandBuffer(); - SendCommand(&c); - return 0; -} - -int CmdHF14AMfAuth4(const char *Cmd) { - uint8_t keyn[20] = {0}; - int keynlen = 0; - uint8_t key[16] = {0}; - int keylen = 0; - - CLIParserInit("hf mf auth4", - "Executes AES authentication command in ISO14443-4", - "Usage:\n\thf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" - "\thf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n"); - - void* argtable[] = { - arg_param_begin, - arg_str1(NULL, NULL, "", NULL), - arg_str1(NULL, NULL, "", NULL), - arg_param_end - }; - CLIExecWithReturn(Cmd, argtable, true); - - CLIGetHexWithReturn(1, keyn, &keynlen); - CLIGetHexWithReturn(2, key, &keylen); - CLIParserFree(); - - if (keynlen != 2) { - PrintAndLogEx(ERR, " must be 2 bytes long instead of: %d", keynlen); - return 1; - } - - if (keylen != 16) { - PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); - return 1; - } - - return MifareAuth4(NULL, keyn, key, true, false, true); -} - -// https://www.nxp.com/docs/en/application-note/AN10787.pdf -int CmdHF14AMfMAD(const char *cmd) { - - CLIParserInit("hf mf mad", - "Checks and prints Mifare Application Directory (MAD)", - "Usage:\n\thf mf mad -> shows MAD if exists\n" - "\thf mf mad -a 03e1 -k ffffffffffff -b -> shows NDEF data if exists. read card with custom key and key B\n"); - - void* argtable[] = { - arg_param_begin, - arg_lit0("vV", "verbose", "show technical data"), - arg_str0("aA", "aid", "print all sectors with aid", NULL), - arg_str0("kK", "key", "key for printing sectors", NULL), - arg_lit0("bB", "keyb", "use key B for access printing sectors (by default: key A)"), - arg_param_end - }; - CLIExecWithReturn(cmd, argtable, true); - bool verbose = arg_get_lit(1); - uint8_t aid[2] = {0}; - int aidlen; - CLIGetHexWithReturn(2, aid, &aidlen); - uint8_t key[6] = {0}; - int keylen; - CLIGetHexWithReturn(3, key, &keylen); - bool keyB = arg_get_lit(4); - - CLIParserFree(); - - if (aidlen != 2 && keylen > 0) { - PrintAndLogEx(WARNING, "do not need a key without aid."); - } - - uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { - PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); - return 2; - } - - if (verbose) { - for(int i = 0; i < 4; i ++) - PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(§or0[i * 16], 16)); - } - - bool haveMAD2 = false; - MAD1DecodeAndPrint(sector0, verbose, &haveMAD2); - - if (haveMAD2) { - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { - PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); - return 2; - } - - MAD2DecodeAndPrint(sector10, verbose); - } - - if (aidlen == 2) { - uint16_t aaid = (aid[0] << 8) + aid[1]; - PrintAndLogEx(NORMAL, "\n-------------- AID 0x%04x ---------------", aaid); - - uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; - size_t madlen = 0; - if (MADDecode(sector0, sector10, mad, &madlen)) { - PrintAndLogEx(ERR, "can't decode mad."); - return 10; - } - - uint8_t akey[6] = {0}; - memcpy(akey, g_mifare_ndef_key, 6); - if (keylen == 6) { - memcpy(akey, key, 6); - } - - for (int i = 0; i < madlen; i++) { - if (aaid == mad[i]) { - uint8_t vsector[16 * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(ERR, "read sector %d error.", i + 1); - return 2; - } - - for(int j = 0; j < (verbose ? 4 : 3); j ++) - PrintAndLogEx(NORMAL, " [%03d] %s", (i + 1) * 4 + j, sprint_hex(&vsector[j * 16], 16)); - } - } - } - - return 0; -} - -int CmdHFMFNDEF(const char *cmd) { - - CLIParserInit("hf mf ndef", - "Prints NFC Data Exchange Format (NDEF)", - "Usage:\n\thf mf ndef -> shows NDEF data\n" - "\thf mf ndef -a 03e1 -k ffffffffffff -b -> shows NDEF data with custom AID, key and with key B\n"); - - void* argtable[] = { - arg_param_begin, - arg_litn("vV", "verbose", 0, 2, "show technical data"), - arg_str0("aA", "aid", "replace default aid for NDEF", NULL), - arg_str0("kK", "key", "replace default key for NDEF", NULL), - arg_lit0("bB", "keyb", "use key B for access sectors (by default: key A)"), - arg_param_end - }; - CLIExecWithReturn(cmd, argtable, true); - - bool verbose = arg_get_lit(1); - bool verbose2 = arg_get_lit(1) > 1; - uint8_t aid[2] = {0}; - int aidlen; - CLIGetHexWithReturn(2, aid, &aidlen); - uint8_t key[6] = {0}; - int keylen; - CLIGetHexWithReturn(3, key, &keylen); - bool keyB = arg_get_lit(4); - - CLIParserFree(); - - uint16_t ndefAID = 0x03e1; - if (aidlen == 2) - ndefAID = (aid[0] << 8) + aid[1]; - - uint8_t ndefkey[6] = {0}; - memcpy(ndefkey, g_mifare_ndef_key, 6); - if (keylen == 6) { - memcpy(ndefkey, key, 6); - } - - uint8_t sector0[16 * 4] = {0}; - uint8_t sector10[16 * 4] = {0}; - uint8_t data[4096] = {0}; - int datalen = 0; - - PrintAndLogEx(NORMAL, ""); - - if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { - PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); - return 2; - } - - bool haveMAD2 = false; - int res = MADCheck(sector0, NULL, verbose, &haveMAD2); - if (res) { - PrintAndLogEx(ERR, "MAD error %d.", res); - return res; - } - - if (haveMAD2) { - if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { - PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); - return 2; - } - } - - uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; - size_t madlen = 0; - if (MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen)) { - PrintAndLogEx(ERR, "can't decode mad."); - return 10; - } - - printf("data reading:"); - for (int i = 0; i < madlen; i++) { - if (ndefAID == mad[i]) { - uint8_t vsector[16 * 4] = {0}; - if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { - PrintAndLogEx(ERR, "read sector %d error.", i + 1); - return 2; - } - - memcpy(&data[datalen], vsector, 16 * 3); - datalen += 16 * 3; - - printf("."); - } - } - printf(" OK\n"); - - if (!datalen) { - PrintAndLogEx(ERR, "no NDEF data."); - return 11; - } - - if (verbose2) { - PrintAndLogEx(NORMAL, "NDEF data:"); - dump_buffer(data, datalen, stdout, 1); - } - - NDEFDecodeAndPrint(data, datalen, verbose); - - return 0; -} - -int CmdHF14AMfList(const char *Cmd) { - CmdTraceList("mf"); - return 0; -} - -static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"list", CmdHF14AMfList, 0, "[Deprecated] List ISO 14443-a / Mifare history"}, - {"darkside", CmdHF14AMfDarkside, 0, "Darkside attack. read parity error messages."}, - {"nested", CmdHF14AMfNested, 0, "Nested attack. Test nested authentication"}, - {"hardnested", CmdHF14AMfNestedHard, 0, "Nested attack for hardened Mifare cards"}, - {"keybrute", CmdHF14AMfKeyBrute, 0, "J_Run's 2nd phase of multiple sector nested authentication key recovery"}, - {"nack", CmdHf14AMfNack, 0, "Test for Mifare NACK bug"}, - {"chk", CmdHF14AMfChk, 0, "Check keys"}, - {"fchk", CmdHF14AMfChk_fast, 0, "Check keys fast, targets all keys on card"}, - {"decrypt", CmdHf14AMfDecryptBytes, 1, "[nt] [ar_enc] [at_enc] [data] - to decrypt snoop or trace"}, - {"-----------", CmdHelp, 1, ""}, - {"dbg", CmdHF14AMfDbg, 0, "Set default debug mode"}, - {"rdbl", CmdHF14AMfRdBl, 0, "Read MIFARE classic block"}, - {"rdsc", CmdHF14AMfRdSc, 0, "Read MIFARE classic sector"}, - {"dump", CmdHF14AMfDump, 0, "Dump MIFARE classic tag to binary file"}, - {"restore", CmdHF14AMfRestore, 0, "Restore MIFARE classic binary file to BLANK tag"}, - {"wrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"}, - {"setmod", CmdHf14AMfSetMod, 0, "Set MIFARE Classic EV1 load modulation strength"}, - {"auth4", CmdHF14AMfAuth4, 0, "ISO14443-4 AES authentication"}, -// {"sniff", CmdHF14AMfSniff, 0, "Sniff card-reader communication"}, - {"-----------", CmdHelp, 1, ""}, - {"sim", CmdHF14AMf1kSim, 0, "Simulate MIFARE card"}, - {"eclr", CmdHF14AMfEClear, 0, "Clear simulator memory block"}, - {"eget", CmdHF14AMfEGet, 0, "Get simulator memory block"}, - {"eset", CmdHF14AMfESet, 0, "Set simulator memory block"}, - {"eload", CmdHF14AMfELoad, 0, "Load from file emul dump"}, - {"esave", CmdHF14AMfESave, 0, "Save to file emul dump"}, - {"ecfill", CmdHF14AMfECFill, 0, "Fill simulator memory with help of keys from simulator"}, - {"ekeyprn", CmdHF14AMfEKeyPrn, 0, "Print keys from simulator memory"}, - {"-----------", CmdHelp, 1, ""}, - {"csetuid", CmdHF14AMfCSetUID, 0, "Set UID for magic Chinese card"}, - {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block - Magic Chinese card"}, - {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block - Magic Chinese card"}, - {"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector - Magic Chinese card"}, - {"cload", CmdHF14AMfCLoad, 0, "Load dump into magic Chinese card"}, - {"csave", CmdHF14AMfCSave, 0, "Save dump from magic Chinese card into file or emulator"}, - {"-----------", CmdHelp, 1, ""}, - {"mad", CmdHF14AMfMAD, 0, "Checks and prints MAD"}, - {"ndef", CmdHFMFNDEF, 0, "Prints NDEF records from card"}, - - {"ice", CmdHF14AMfice, 0, "collect Mifare Classic nonces to file"}, - {NULL, NULL, 0, NULL} -}; - -int CmdHFMF(const char *Cmd) { - clearCommandBuffer(); - CmdsParse(CommandTable, Cmd); - return 0; -} - -int CmdHelp(const char *Cmd) { - CmdsHelp(CommandTable); - return 0; -} +//----------------------------------------------------------------------------- +// Copyright (C) 2011,2012 Merlok +// +// 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 MIFARE commands +//----------------------------------------------------------------------------- + +#include "cmdhfmf.h" +#include "mifare/mifare4.h" +#include "mifare/mad.h" +#include "mifare/ndef.h" + + +#define MFBLOCK_SIZE 16 + +#define MIFARE_4K_MAXBLOCK 256 +#define MIFARE_2K_MAXBLOCK 128 +#define MIFARE_1K_MAXBLOCK 64 +#define MIFARE_MINI_MAXBLOCK 20 + +#define MIFARE_MINI_MAXSECTOR 5 +#define MIFARE_1K_MAXSECTOR 16 +#define MIFARE_2K_MAXSECTOR 32 +#define MIFARE_4K_MAXSECTOR 40 + +static int CmdHelp(const char *Cmd); + +int usage_hf14_ice(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf ice [l] [f] "); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " l nonces to be collected"); + PrintAndLogEx(NORMAL, " f save nonces to instead of hf-mf--nonces.bin"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf ice"); + PrintAndLogEx(NORMAL, " hf mf ice f nonces.bin"); + return 0; +} + +int usage_hf14_dump(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf dump [card memory] k f "); + PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLogEx(NORMAL, " k : key filename, if no given, UID will be used as filename"); + PrintAndLogEx(NORMAL, " f : data filename, if no given, UID will be used as filename"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf dump"); + PrintAndLogEx(NORMAL, " hf mf dump 4"); + return 0; +} + +int usage_hf14_mifare(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf darkside [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " (Optional) target other block"); + PrintAndLogEx(NORMAL, " (optional) target key type"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf darkside"); + PrintAndLogEx(NORMAL, " hf mf darkside 16"); + PrintAndLogEx(NORMAL, " hf mf darkside 16 B"); + return 0; +} +int usage_hf14_mf1ksim(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf sim [h] u n [i] [x] [e] [v]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " u (Optional) UID 4,7 or 10bytes. If not specified, the UID 4b from emulator memory will be used"); + PrintAndLogEx(NORMAL, " n (Optional) Automatically exit simulation after blocks have been read by reader. 0 = infinite"); + PrintAndLogEx(NORMAL, " i (Optional) Interactive, means that console will not be returned until simulation finishes or is aborted"); + PrintAndLogEx(NORMAL, " x (Optional) Crack, performs the 'reader attack', nr/ar attack against a reader"); + PrintAndLogEx(NORMAL, " e (Optional) Fill simulator keys from found keys"); + PrintAndLogEx(NORMAL, " v (Optional) Verbose"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf sim u 0a0a0a0a"); + PrintAndLogEx(NORMAL, " hf mf sim u 11223344556677"); + PrintAndLogEx(NORMAL, " hf mf sim u 112233445566778899AA"); + PrintAndLogEx(NORMAL, " hf mf sim u 11223344 i x"); + return 0; +} +int usage_hf14_dbg(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf dbg [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " (Optional) see list for valid levels"); + PrintAndLogEx(NORMAL, " 0 - no debug messages"); + PrintAndLogEx(NORMAL, " 1 - error messages"); + PrintAndLogEx(NORMAL, " 2 - plus information messages"); + PrintAndLogEx(NORMAL, " 3 - plus debug messages"); + PrintAndLogEx(NORMAL, " 4 - print even debug messages in timing critical functions"); + PrintAndLogEx(NORMAL, " Note: this option therefore may cause malfunction itself"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf dbg 3"); + return 0; +} +int usage_hf14_sniff(void){ + PrintAndLogEx(NORMAL, "It continuously gets data from the field and saves it to: log, emulator, emulator file."); + PrintAndLogEx(NORMAL, "Usage: hf mf sniff [h] [l] [d] [f]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " l save encrypted sequence to logfile `uid.log`"); + PrintAndLogEx(NORMAL, " d decrypt sequence and put it to log file `uid.log`"); +// PrintAndLogEx(NORMAL, " n/a e decrypt sequence, collect read and write commands and save the result of the sequence to emulator memory"); + PrintAndLogEx(NORMAL, " f decrypt sequence, collect read and write commands and save the result of the sequence to emulator dump file `uid.eml`"); + PrintAndLogEx(NORMAL, "Example:"); + PrintAndLogEx(NORMAL, " hf mf sniff l d f"); + return 0; +} +int usage_hf14_nested(void){ + PrintAndLogEx(NORMAL, "Usage:"); + PrintAndLogEx(NORMAL, " all sectors: hf mf nested [t,d]"); + PrintAndLogEx(NORMAL, " one sector: hf mf nested o "); + PrintAndLogEx(NORMAL, " [t]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " card memory - 0 - MINI(320 bytes), 1 - 1K, 2 - 2K, 4 - 4K, - 1K"); + PrintAndLogEx(NORMAL, " t transfer keys into emulator memory"); + PrintAndLogEx(NORMAL, " d write keys to binary file `hf-mf--key.bin`"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf nested 1 0 A FFFFFFFFFFFF "); + PrintAndLogEx(NORMAL, " hf mf nested 1 0 A FFFFFFFFFFFF t "); + PrintAndLogEx(NORMAL, " hf mf nested 1 0 A FFFFFFFFFFFF d "); + PrintAndLogEx(NORMAL, " hf mf nested o 0 A FFFFFFFFFFFF 4 A"); + return 0; +} +int usage_hf14_hardnested(void){ + PrintAndLogEx(NORMAL, "Usage:"); + PrintAndLogEx(NORMAL, " hf mf hardnested "); + PrintAndLogEx(NORMAL, " [known target key (12 hex symbols)] [w] [s]"); + PrintAndLogEx(NORMAL, " or hf mf hardnested r [known target key]"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " w acquire nonces and UID, and write them to binary file with default name hf-mf--nonces.bin"); + PrintAndLogEx(NORMAL, " s slower acquisition (required by some non standard cards)"); + PrintAndLogEx(NORMAL, " r read hf-mf--nonces.bin if tag present, otherwise read nonces.bin, then start attack"); + PrintAndLogEx(NORMAL, " u read/write hf-mf--nonces.bin instead of default name"); + PrintAndLogEx(NORMAL, " f read/write instead of default name"); + PrintAndLogEx(NORMAL, " t tests?"); + PrintAndLogEx(NORMAL, " i set type of SIMD instructions. Without this flag programs autodetect it."); + PrintAndLogEx(NORMAL, " i 5 = AVX512"); + PrintAndLogEx(NORMAL, " i 2 = AVX2"); + PrintAndLogEx(NORMAL, " i a = AVX"); + PrintAndLogEx(NORMAL, " i s = SSE2"); + PrintAndLogEx(NORMAL, " i m = MMX"); + PrintAndLogEx(NORMAL, " i n = none (use CPU regular instruction set)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf hardnested 0 A FFFFFFFFFFFF 4 A"); + PrintAndLogEx(NORMAL, " hf mf hardnested 0 A FFFFFFFFFFFF 4 A w"); + PrintAndLogEx(NORMAL, " hf mf hardnested 0 A FFFFFFFFFFFF 4 A f nonces.bin w s"); + PrintAndLogEx(NORMAL, " hf mf hardnested r"); + PrintAndLogEx(NORMAL, " hf mf hardnested r a0a1a2a3a4a5"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Add the known target key to check if it is present in the remaining key space:"); + PrintAndLogEx(NORMAL, " hf mf hardnested 0 A A0A1A2A3A4A5 4 A FFFFFFFFFFFF"); + return 0; +} +int usage_hf14_chk(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf chk [h] |<*card memory> [t|d] [] []"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " * all sectors based on card memory, other values then below defaults to 1k"); + PrintAndLogEx(NORMAL, " 0 - MINI(320 bytes)"); + PrintAndLogEx(NORMAL, " 1 - 1K"); + PrintAndLogEx(NORMAL, " 2 - 2K"); + PrintAndLogEx(NORMAL, " 4 - 4K"); + PrintAndLogEx(NORMAL, " d write keys to binary file"); + PrintAndLogEx(NORMAL, " t write keys to emulator memory\n"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf chk 0 A 1234567890ab keys.dic -- target block 0, Key A"); + PrintAndLogEx(NORMAL, " hf mf chk *1 ? t -- target all blocks, all keys, 1K, write to emul"); + PrintAndLogEx(NORMAL, " hf mf chk *1 ? d -- target all blocks, all keys, 1K, write to file"); + return 0; +} +int usage_hf14_chk_fast(void){ + PrintAndLogEx(NORMAL, "This is a improved checkkeys method speedwise. It checks Mifare Classic tags sector keys against a dictionary file with keys"); + PrintAndLogEx(NORMAL, "Usage: hf mf fchk [h] [t|d|f] [] []"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " all sectors based on card memory, other values than below defaults to 1k"); + PrintAndLogEx(NORMAL, " 0 - MINI(320 bytes)"); + PrintAndLogEx(NORMAL, " 1 - 1K "); + PrintAndLogEx(NORMAL, " 2 - 2K"); + PrintAndLogEx(NORMAL, " 4 - 4K"); + PrintAndLogEx(NORMAL, " d write keys to binary file"); + PrintAndLogEx(NORMAL, " t write keys to emulator memory"); + PrintAndLogEx(NORMAL, " m use dictionary from flashmemory\n"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf fchk 1 1234567890ab keys.dic -- target 1K using key 1234567890ab, using dictionary file"); + PrintAndLogEx(NORMAL, " hf mf fchk 1 t -- target 1K, write to emulator memory"); + PrintAndLogEx(NORMAL, " hf mf fchk 1 d -- target 1K, write to file"); +#ifdef WITH_FLASH + PrintAndLogEx(NORMAL, " hf mf fchk 1 m -- target 1K, use dictionary from flashmemory"); +#endif + return 0; +} +int usage_hf14_keybrute(void){ + PrintAndLogEx(NORMAL, "J_Run's 2nd phase of multiple sector nested authentication key recovery"); + PrintAndLogEx(NORMAL, "You have a known 4 last bytes of a key recovered with mf_nonce_brute tool."); + PrintAndLogEx(NORMAL, "First 2 bytes of key will be bruteforced"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, " ---[ This attack is obsolete, try hardnested instead ]---"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf keybrute [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " target block number"); + PrintAndLogEx(NORMAL, " target key type"); + PrintAndLogEx(NORMAL, " candidate key from mf_nonce_brute tool"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf keybrute 1 A 000011223344"); + return 0; +} +int usage_hf14_restore(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf restore [card memory] u k f "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLogEx(NORMAL, " u : uid, try to restore from hf-mf--key.bin and hf-mf--data.bin"); + PrintAndLogEx(NORMAL, " k : key filename, specific the full filename of key file"); + PrintAndLogEx(NORMAL, " f : data filename, specific the full filename of data file"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf restore -- read the UID from tag first, then restore from hf-mf--key.bin and and hf-mf--data.bin"); + PrintAndLogEx(NORMAL, " hf mf restore 1 u 12345678 -- restore from hf-mf-12345678-key.bin and hf-mf-12345678-data.bin"); + PrintAndLogEx(NORMAL, " hf mf restore 1 u 12345678 k dumpkey.bin -- restore from dumpkey.bin and hf-mf-12345678-data.bin"); + PrintAndLogEx(NORMAL, " hf mf restore 4 -- read the UID from tag with 4K memory first, then restore from hf-mf--key.bin and and hf-mf--data.bin"); + return 0; +} +int usage_hf14_decryptbytes(void){ + PrintAndLogEx(NORMAL, "Decrypt Crypto-1 encrypted bytes given some known state of crypto. See tracelog to gather needed values\n"); + PrintAndLogEx(NORMAL, "Usage: hf mf decrypt [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " reader nonce"); + PrintAndLogEx(NORMAL, " encrypted reader response"); + PrintAndLogEx(NORMAL, " encrypted tag response"); + PrintAndLogEx(NORMAL, " encrypted data, taken directly after at_enc and forward"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf decrypt b830049b 9248314a 9280e203 41e586f9\n"); + PrintAndLogEx(NORMAL, " this sample decrypts 41e586f9 -> 3003999a Annotated: 30 03 [99 9a] auth block 3 [crc]"); + return 0; +} + +int usage_hf14_eget(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf eget "); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf eget 0 "); + return 0; +} +int usage_hf14_eclr(void){ + PrintAndLogEx(NORMAL, "It set card emulator memory to empty data blocks and key A/B FFFFFFFFFFFF \n"); + PrintAndLogEx(NORMAL, "Usage: hf mf eclr"); + return 0; +} +int usage_hf14_eset(void){ + PrintAndLogEx(NORMAL, "Usage: hf mf eset "); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf eset 1 000102030405060708090a0b0c0d0e0f "); + return 0; +} +int usage_hf14_eload(void){ + PrintAndLogEx(NORMAL, "It loads emul dump from the file `filename.eml`"); + PrintAndLogEx(NORMAL, "Usage: hf mf eload [card memory] [numblocks]"); + PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K, u = UL"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf eload filename"); + PrintAndLogEx(NORMAL, " hf mf eload 4 filename"); + return 0; +} +int usage_hf14_esave(void){ + PrintAndLogEx(NORMAL, "It saves emul dump into the file `filename.eml` or `cardID.eml`"); + PrintAndLogEx(NORMAL, " Usage: hf mf esave [card memory] [file name w/o `.eml`]"); + PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf esave "); + PrintAndLogEx(NORMAL, " hf mf esave 4"); + PrintAndLogEx(NORMAL, " hf mf esave 4 filename"); + return 0; +} +int usage_hf14_ecfill(void){ + PrintAndLogEx(NORMAL, "Read card and transfer its data to emulator memory."); + PrintAndLogEx(NORMAL, "Keys must be laid in the emulator memory. \n"); + PrintAndLogEx(NORMAL, "Usage: hf mf ecfill [card memory]"); + PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf ecfill A"); + PrintAndLogEx(NORMAL, " hf mf ecfill A 4"); + return 0; +} +int usage_hf14_ekeyprn(void){ + PrintAndLogEx(NORMAL, "It prints the keys loaded in the emulator memory"); + PrintAndLogEx(NORMAL, "Usage: hf mf ekeyprn [card memory]"); + PrintAndLogEx(NORMAL, " [card memory]: 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf ekeyprn 1"); + return 0; +} + +int usage_hf14_csetuid(void){ + PrintAndLogEx(NORMAL, "Set UID, ATQA, and SAK for magic Chinese card. Only works with magic cards"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf csetuid [h] [ATQA 4 hex symbols] [SAK 2 hex symbols] [w]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " w wipe card before writing"); + PrintAndLogEx(NORMAL, " UID 8 hex symbols"); + PrintAndLogEx(NORMAL, " ATQA 4 hex symbols"); + PrintAndLogEx(NORMAL, " SAK 2 hex symbols"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf csetuid 01020304"); + PrintAndLogEx(NORMAL, " hf mf csetuid 01020304 0004 08 w"); + return 0; +} +int usage_hf14_csetblk(void){ + PrintAndLogEx(NORMAL, "Set block data for magic Chinese card. Only works with magic cards"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf csetblk [h] [w]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " w wipe card before writing"); + PrintAndLogEx(NORMAL, " block number"); + PrintAndLogEx(NORMAL, " block data to write (32 hex symbols)"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf csetblk 1 01020304050607080910111213141516"); + PrintAndLogEx(NORMAL, " hf mf csetblk 1 01020304050607080910111213141516 w"); + return 0; +} +int usage_hf14_cload(void){ + PrintAndLogEx(NORMAL, "It loads magic Chinese card from the file `filename.eml`"); + PrintAndLogEx(NORMAL, "or from emulator memory"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf cload [h] [e] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " e load card with data from emulator memory"); + PrintAndLogEx(NORMAL, " j load card with data from json file"); + PrintAndLogEx(NORMAL, " b load card with data from binary file"); + PrintAndLogEx(NORMAL, " load card with data from eml file"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf cload mydump"); + PrintAndLogEx(NORMAL, " hf mf cload e"); + return 0; +} +int usage_hf14_cgetblk(void){ + PrintAndLogEx(NORMAL, "Get block data from magic Chinese card. Only works with magic cards\n"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf cgetblk [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " block number"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf cgetblk 1"); + return 0; +} +int usage_hf14_cgetsc(void){ + PrintAndLogEx(NORMAL, "Get sector data from magic Chinese card. Only works with magic cards\n"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf cgetsc [h] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " sector number"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf cgetsc 0"); + return 0; +} +int usage_hf14_csave(void){ + PrintAndLogEx(NORMAL, "It saves `magic Chinese` card dump into the file `filename.eml` or `cardID.eml`"); + PrintAndLogEx(NORMAL, "or into emulator memory"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf csave [h] [e] [u] [card memory] i "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " e save data to emulator memory"); + PrintAndLogEx(NORMAL, " u save data to file, use carduid as filename"); + PrintAndLogEx(NORMAL, " card memory 0 = 320 bytes (Mifare Mini), 1 = 1K (default), 2 = 2K, 4 = 4K"); + PrintAndLogEx(NORMAL, " o save data to file"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf csave u 1"); + PrintAndLogEx(NORMAL, " hf mf csave e 1"); + PrintAndLogEx(NORMAL, " hf mf csave 4 o filename"); + return 0; +} +int usage_hf14_nack(void) { + PrintAndLogEx(NORMAL, "Test a mifare classic based card for the NACK bug."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Usage: hf mf nack [h] [v]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h this help"); + PrintAndLogEx(NORMAL, " v verbose"); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf nack"); + return 0; +} + +int GetHFMF14AUID(uint8_t *uid, int *uidlen) { + UsbCommand c = {CMD_READER_ISO_14443a, {ISO14A_CONNECT, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + if (!WaitForResponseTimeout(CMD_ACK, &resp, 2500)) { + PrintAndLogEx(WARNING, "iso14443a card select failed"); + DropField(); + return 0; + } + + iso14a_card_select_t card; + memcpy(&card, (iso14a_card_select_t *)resp.d.asBytes, sizeof(iso14a_card_select_t)); + memcpy(uid, card.uid, card.uidlen * sizeof(uint8_t)); + *uidlen = card.uidlen; + return 1; +} + +char * GenerateFilename(const char *prefix, const char *suffix){ + uint8_t uid[10] = {0,0,0,0,0,0,0,0,0,0}; + int uidlen=0; + char * fptr = calloc (sizeof (char) * (strlen(prefix) + strlen(suffix)) + sizeof(uid)*2 + 1, sizeof(uint8_t)); + + GetHFMF14AUID(uid, &uidlen); + if (!uidlen) { + PrintAndLogEx(WARNING, "No tag found."); + free(fptr); + return NULL; + } + + strcpy(fptr, prefix); + FillFileNameByUID(fptr, uid, suffix, uidlen); + return fptr; +} + +int CmdHF14AMfDarkside(const char *Cmd) { + uint8_t blockno = 0, key_type = MIFARE_AUTH_KEYA; + uint64_t key = 0; + + char cmdp = tolower(param_getchar(Cmd, 0)); + if ( cmdp == 'h' ) return usage_hf14_mifare(); + + blockno = param_get8(Cmd, 0); + + cmdp = tolower(param_getchar(Cmd, 1)); + if (cmdp == 'b') + key_type = MIFARE_AUTH_KEYB; + + int isOK = mfDarkside(blockno, key_type, &key); + PrintAndLogEx(NORMAL, ""); + switch (isOK) { + case -1 : PrintAndLogEx(WARNING, "button pressed. Aborted."); return 1; + case -2 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests)."); return 1; + case -3 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator is not predictable)."); return 1; + case -4 : PrintAndLogEx(FAILED, "card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown"); + PrintAndLogEx(FAILED, "generating polynomial with 16 effective bits only, but shows unexpected behaviour."); return 1; + case -5 : PrintAndLogEx(WARNING, "aborted via keyboard."); return 1; + default : PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 "\n", key); break; + } + PrintAndLogEx(NORMAL, ""); + return 0; +} + +int CmdHF14AMfWrBl(const char *Cmd) { + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t bldata[16] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; + char cmdp = 0x00; + + if (strlen(Cmd) < 3) { + PrintAndLogEx(NORMAL, "Usage: hf mf wrbl "); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf wrbl 0 A FFFFFFFFFFFF 000102030405060708090A0B0C0D0E0F"); + return 0; + } + + blockNo = param_get8(Cmd, 0); + cmdp = tolower(param_getchar(Cmd, 1)); + if (cmdp == 0x00) { + PrintAndLogEx(NORMAL, "Key type must be A or B"); + return 1; + } + + if (cmdp != 'a') + keyType = 1; + + if (param_gethex(Cmd, 2, key, 12)) { + PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); + return 1; + } + + if (param_gethex(Cmd, 3, bldata, 32)) { + PrintAndLogEx(NORMAL, "Block data must include 32 HEX symbols"); + return 1; + } + + PrintAndLogEx(NORMAL, "--block no:%d, key type:%c, key:%s", blockNo, keyType?'B':'A', sprint_hex(key, 6)); + PrintAndLogEx(NORMAL, "--data: %s", sprint_hex(bldata, 16)); + + UsbCommand c = {CMD_MIFARE_WRITEBL, {blockNo, keyType, 0}}; + memcpy(c.d.asBytes, key, 6); + memcpy(c.d.asBytes + 10, bldata, 16); + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLogEx(NORMAL, "isOk:%02x", isOK); + } else { + PrintAndLogEx(NORMAL, "Command execute timeout"); + } + + return 0; +} + +int CmdHF14AMfRdBl(const char *Cmd) { + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + char cmdp = 0x00; + + if (strlen(Cmd)<3) { + PrintAndLogEx(NORMAL, "Usage: hf mf rdbl "); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf rdbl 0 A FFFFFFFFFFFF "); + return 0; + } + + blockNo = param_get8(Cmd, 0); + cmdp = tolower(param_getchar(Cmd, 1)); + if (cmdp == 0x00) { + PrintAndLogEx(NORMAL, "Key type must be A or B"); + return 1; + } + + if (cmdp != 'a') + keyType = 1; + + if (param_gethex(Cmd, 2, key, 12)) { + PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); + return 1; + } + PrintAndLogEx(NORMAL, "--block no:%d, key type:%c, key:%s ", blockNo, keyType?'B':'A', sprint_hex(key, 6)); + + UsbCommand c = {CMD_MIFARE_READBL, {blockNo, keyType, 0}}; + memcpy(c.d.asBytes, key, 6); + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + uint8_t *data = resp.d.asBytes; + + if (isOK) { + PrintAndLogEx(NORMAL, "isOk:%02x data:%s", isOK, sprint_hex(data, 16)); + } else { + PrintAndLogEx(NORMAL, "isOk:%02x", isOK); + return 1; + } + + if (mfIsSectorTrailer(blockNo) && (data[6] || data[7] || data[8])) { + PrintAndLogEx(NORMAL, "Trailer decoded:"); + int bln = mfFirstBlockOfSector(mfSectorNum(blockNo)); + int blinc = (mfNumBlocksPerSector(mfSectorNum(blockNo)) > 4) ? 5 : 1; + for (int i = 0; i < 4; i++) { + PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &data[6])); + bln += blinc; + } + PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&data[9], 1)); + } + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + return 2; + } + + return 0; +} + +int CmdHF14AMfRdSc(const char *Cmd) { + int i; + uint8_t sectorNo = 0; + uint8_t keyType = 0; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t isOK = 0; + uint8_t *data = NULL; + char cmdp = 0x00; + + if (strlen(Cmd) < 3) { + PrintAndLogEx(NORMAL, "Usage: hf mf rdsc "); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " hf mf rdsc 0 A FFFFFFFFFFFF "); + return 0; + } + + sectorNo = param_get8(Cmd, 0); + if (sectorNo > MIFARE_4K_MAXSECTOR ) { + PrintAndLogEx(NORMAL, "Sector number must be less than 40"); + return 1; + } + + cmdp = tolower(param_getchar(Cmd, 1)); + if (cmdp != 'a' && cmdp != 'b') { + PrintAndLogEx(NORMAL, "Key type must be A or B"); + return 1; + } + + if (cmdp != 'a') + keyType = 1; + + if (param_gethex(Cmd, 2, key, 12)) { + PrintAndLogEx(NORMAL, "Key must include 12 HEX symbols"); + return 1; + } + PrintAndLogEx(NORMAL, "--sector no:%d key type:%c key:%s ", sectorNo, keyType?'B':'A', sprint_hex(key, 6)); + + UsbCommand c = {CMD_MIFARE_READSC, {sectorNo, keyType, 0}}; + memcpy(c.d.asBytes, key, 6); + clearCommandBuffer(); + SendCommand(&c); + PrintAndLogEx(NORMAL, ""); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + isOK = resp.arg[0] & 0xff; + data = resp.d.asBytes; + + PrintAndLogEx(NORMAL, "isOk:%02x", isOK); + if (isOK) { + for (i = 0; i < (sectorNo<32?3:15); i++) { + PrintAndLogEx(NORMAL, "data : %s", sprint_hex(data + i * 16, 16)); + } + PrintAndLogEx(NORMAL, "trailer: %s", sprint_hex(data + (sectorNo<32?3:15) * 16, 16)); + + PrintAndLogEx(NORMAL, "Trailer decoded:"); + int bln = mfFirstBlockOfSector(sectorNo); + int blinc = (mfNumBlocksPerSector(sectorNo) > 4) ? 5 : 1; + for (i = 0; i < 4; i++) { + PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &(data + (sectorNo<32?3:15) * 16)[6])); + bln += blinc; + } + PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&(data + (sectorNo<32?3:15) * 16)[9], 1)); + } + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + + return 0; +} + +uint16_t NumOfBlocks(char card){ + switch(card){ + case '0' : return MIFARE_MINI_MAXBLOCK; + case '1' : return MIFARE_1K_MAXBLOCK; + case '2' : return MIFARE_2K_MAXBLOCK; + case '4' : return MIFARE_4K_MAXBLOCK; + default : return MIFARE_1K_MAXBLOCK; + } +} +uint8_t NumOfSectors(char card){ + switch(card){ + case '0' : return MIFARE_MINI_MAXSECTOR; + case '1' : return MIFARE_1K_MAXSECTOR; + case '2' : return MIFARE_2K_MAXSECTOR; + case '4' : return MIFARE_4K_MAXSECTOR; + default : return MIFARE_1K_MAXSECTOR; + } +} + +uint8_t FirstBlockOfSector(uint8_t sectorNo) { + if (sectorNo < 32) { + return sectorNo * 4; + } else { + return 32 * 4 + (sectorNo - 32) * 16; + } +} + +uint8_t NumBlocksPerSector(uint8_t sectorNo) { + if (sectorNo < 32) { + return 4; + } else { + return 16; + } +} + +int CmdHF14AMfDump(const char *Cmd) { + + uint8_t sectorNo, blockNo; + uint8_t keyA[40][6]; + uint8_t keyB[40][6]; + uint8_t rights[40][4]; + uint8_t carddata[256][16]; + uint8_t numSectors = 16; + uint8_t cmdp = 0; + + char keyFilename[FILE_PATH_SIZE] = {0}; + char dataFilename[FILE_PATH_SIZE]; + char * fptr; + + memset(keyFilename, 0, sizeof(keyFilename)); + memset(dataFilename, 0, sizeof(dataFilename)); + + FILE *f; + UsbCommand resp; + + while(param_getchar(Cmd, cmdp) != 0x00) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_hf14_dump(); + case 'k': + param_getstr(Cmd, cmdp+1, keyFilename, FILE_PATH_SIZE); + cmdp += 2; + break; + case 'f': + param_getstr(Cmd, cmdp+1, dataFilename, FILE_PATH_SIZE); + cmdp += 2; + break; + default: + if (cmdp == 0) { + numSectors = NumOfSectors(param_getchar(Cmd, cmdp)); + cmdp++; + } else { + PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + return usage_hf14_dump(); + } + } + } + + if ( keyFilename[0] == 0x00 ) { + fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) + return 1; + + strcpy(keyFilename, fptr); + } + + if ((f = fopen(keyFilename, "rb")) == NULL) { + PrintAndLogEx(WARNING, "Could not find file " _YELLOW_(%s), keyFilename); + return 1; + } + + // Read keys A from file + size_t bytes_read; + for (sectorNo=0; sectorNo> 2) | ((data[8] & 0x1) << 1) | ((data[8] & 0x10) >> 4); // C1C2C3 for data area 0 + rights[sectorNo][1] = ((data[7] & 0x20) >> 3) | ((data[8] & 0x2) << 0) | ((data[8] & 0x20) >> 5); // C1C2C3 for data area 1 + rights[sectorNo][2] = ((data[7] & 0x40) >> 4) | ((data[8] & 0x4) >> 1) | ((data[8] & 0x40) >> 6); // C1C2C3 for data area 2 + rights[sectorNo][3] = ((data[7] & 0x80) >> 5) | ((data[8] & 0x8) >> 2) | ((data[8] & 0x80) >> 7); // C1C2C3 for sector trailer + break; + } else if (tries == 2) { // on last try set defaults + PrintAndLogEx(FAILED, "could not get access rights for sector %2d. Trying with defaults...", sectorNo); + rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; + rights[sectorNo][3] = 0x01; + } + } else { + PrintAndLogEx(FAILED, "command execute timeout when trying to read access rights for sector %2d. Trying with defaults...", sectorNo); + rights[sectorNo][0] = rights[sectorNo][1] = rights[sectorNo][2] = 0x00; + rights[sectorNo][3] = 0x01; + } + } + } + + PrintAndLogEx(SUCCESS, "Finished reading sector access bits"); + PrintAndLogEx(INFO, "Dumping all blocks from card..."); + + bool isOK = true; + for (sectorNo = 0; isOK && sectorNo < numSectors; sectorNo++) { + for (blockNo = 0; isOK && blockNo < NumBlocksPerSector(sectorNo); blockNo++) { + bool received = false; + + for (tries = 0; tries < MIFARE_SECTOR_RETRY; tries++) { + if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer. At least the Access Conditions can always be read with key A. + UsbCommand c = {CMD_MIFARE_READBL, {FirstBlockOfSector(sectorNo) + blockNo, 0, 0}}; + memcpy(c.d.asBytes, keyA[sectorNo], 6); + clearCommandBuffer(); + SendCommand(&c); + received = WaitForResponseTimeout(CMD_ACK, &resp, 1500); + } else { // data block. Check if it can be read with key A or key B + uint8_t data_area = (sectorNo < 32) ? blockNo : blockNo/5; + if ((rights[sectorNo][data_area] == 0x03) || (rights[sectorNo][data_area] == 0x05)) { // only key B would work + UsbCommand c = {CMD_MIFARE_READBL, {FirstBlockOfSector(sectorNo) + blockNo, 1, 0}}; + memcpy(c.d.asBytes, keyB[sectorNo], 6); + SendCommand(&c); + received = WaitForResponseTimeout(CMD_ACK, &resp, 1500); + } else if (rights[sectorNo][data_area] == 0x07) { // no key would work + isOK = false; + PrintAndLogEx(WARNING, "access rights do not allow reading of sector %2d block %3d", sectorNo, blockNo); + tries = MIFARE_SECTOR_RETRY; + } else { // key A would work + UsbCommand c = {CMD_MIFARE_READBL, {FirstBlockOfSector(sectorNo) + blockNo, 0, 0}}; + memcpy(c.d.asBytes, keyA[sectorNo], 6); + clearCommandBuffer(); + SendCommand(&c); + received = WaitForResponseTimeout(CMD_ACK, &resp, 1500); + } + } + if (received) { + isOK = resp.arg[0] & 0xff; + if (isOK) break; + } + } + + if (received) { + isOK = resp.arg[0] & 0xff; + uint8_t *data = resp.d.asBytes; + if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer. Fill in the keys. + data[0] = (keyA[sectorNo][0]); + data[1] = (keyA[sectorNo][1]); + data[2] = (keyA[sectorNo][2]); + data[3] = (keyA[sectorNo][3]); + data[4] = (keyA[sectorNo][4]); + data[5] = (keyA[sectorNo][5]); + data[10] = (keyB[sectorNo][0]); + data[11] = (keyB[sectorNo][1]); + data[12] = (keyB[sectorNo][2]); + data[13] = (keyB[sectorNo][3]); + data[14] = (keyB[sectorNo][4]); + data[15] = (keyB[sectorNo][5]); + } + if (isOK) { + memcpy(carddata[FirstBlockOfSector(sectorNo) + blockNo], data, 16); + PrintAndLogEx(SUCCESS, "successfully read block %2d of sector %2d.", blockNo, sectorNo); + } else { + PrintAndLogEx(FAILED, "could not read block %2d of sector %2d", blockNo, sectorNo); + break; + } + } + else { + isOK = false; + PrintAndLogEx(WARNING, "command execute timeout when trying to read block %2d of sector %2d.", blockNo, sectorNo); + break; + } + } + } + + if (isOK == 0) { + PrintAndLogEx(FAILED, "Something went wrong"); + return 0; + } + + PrintAndLogEx(SUCCESS, "\nSuccedded in dumping all blocks"); + + if ( strlen(dataFilename) < 1 ) { + fptr = dataFilename; + fptr += sprintf(fptr, "hf-mf-"); + FillFileNameByUID(fptr, (uint8_t *)carddata, "-data", 4); + } + + uint16_t bytes = 16*(FirstBlockOfSector(numSectors - 1) + NumBlocksPerSector(numSectors - 1)); + + saveFile(dataFilename, "bin", (uint8_t *)carddata, bytes); + saveFileEML(dataFilename, "eml", (uint8_t *)carddata, bytes, MFBLOCK_SIZE); + saveFileJSON(dataFilename, "json", jsfCardMemory, (uint8_t *)carddata, bytes); + return 0; +} + +int CmdHF14AMfRestore(const char *Cmd) { + uint8_t sectorNo,blockNo; + uint8_t keyType = 0; + uint8_t key[6] = {0xFF,0xFF,0xFF,0xFF,0xFF,0xFF}; + uint8_t bldata[16] = {0x00}; + uint8_t keyA[40][6]; + uint8_t keyB[40][6]; + uint8_t numSectors = 16; + uint8_t cmdp = 0; + char keyFilename[FILE_PATH_SIZE] = ""; + char dataFilename[FILE_PATH_SIZE] = ""; + char szTemp[FILE_PATH_SIZE-20] = ""; + char *fptr; + FILE *fdump, *fkeys; + + while(param_getchar(Cmd, cmdp) != 0x00) { + switch(tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_hf14_restore(); + case 'u': + param_getstr(Cmd, cmdp+1, szTemp, FILE_PATH_SIZE-20); + if(keyFilename[0]==0x00) + snprintf(keyFilename, FILE_PATH_SIZE, "hf-mf-%s-key.bin", szTemp); + if(dataFilename[0]==0x00) + snprintf(dataFilename, FILE_PATH_SIZE, "hf-mf-%s-data.bin", szTemp); + cmdp+=2; + break; + case 'k': + param_getstr(Cmd, cmdp+1, keyFilename, FILE_PATH_SIZE); + cmdp += 2; + break; + case 'f': + param_getstr(Cmd, cmdp+1, dataFilename, FILE_PATH_SIZE); + cmdp += 2; + break; + default: + if ( cmdp == 0 ) { + numSectors = NumOfSectors(param_getchar(Cmd, cmdp)); + cmdp++; + } else { + PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", param_getchar(Cmd, cmdp)); + return usage_hf14_restore(); + } + } + } + + if ( keyFilename[0] == 0x00 ) { + fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) + return 1; + + strcpy(keyFilename, fptr); + } + + if ((fkeys = fopen(keyFilename, "rb")) == NULL) { + PrintAndLogEx(WARNING, "Could not find file " _YELLOW_(%s), keyFilename); + return 1; + } + + size_t bytes_read; + for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { + bytes_read = fread( keyA[sectorNo], 1, 6, fkeys ); + if ( bytes_read != 6 ) { + PrintAndLogEx(WARNING, "File reading error " _YELLOW_(%s), keyFilename); + fclose(fkeys); + return 2; + } + } + + for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { + bytes_read = fread( keyB[sectorNo], 1, 6, fkeys ); + if ( bytes_read != 6 ) { + PrintAndLogEx(WARNING, "File reading error " _YELLOW_(%s), keyFilename); + fclose(fkeys); + return 2; + } + } + + fclose(fkeys); + + if ( dataFilename[0] == 0x00 ) { + fptr = GenerateFilename("hf-mf-", "-data.bin"); + if (fptr == NULL) + return 1; + + strcpy(dataFilename,fptr); + } + + if ((fdump = fopen(dataFilename, "rb")) == NULL) { + PrintAndLogEx(WARNING, "Could not find file " _YELLOW_(%s), dataFilename); + return 1; + } + PrintAndLogEx(INFO, "Restoring " _YELLOW_(%s)" to card", dataFilename); + + for (sectorNo = 0; sectorNo < numSectors; sectorNo++) { + for (blockNo = 0; blockNo < NumBlocksPerSector(sectorNo); blockNo++) { + UsbCommand c = {CMD_MIFARE_WRITEBL, {FirstBlockOfSector(sectorNo) + blockNo, keyType, 0}}; + memcpy(c.d.asBytes, key, 6); + bytes_read = fread(bldata, 1, 16, fdump); + if ( bytes_read != 16) { + PrintAndLogEx(WARNING, "File reading error " _YELLOW_(%s), dataFilename); + fclose(fdump); + fdump = NULL; + return 2; + } + + if (blockNo == NumBlocksPerSector(sectorNo) - 1) { // sector trailer + bldata[0] = (keyA[sectorNo][0]); + bldata[1] = (keyA[sectorNo][1]); + bldata[2] = (keyA[sectorNo][2]); + bldata[3] = (keyA[sectorNo][3]); + bldata[4] = (keyA[sectorNo][4]); + bldata[5] = (keyA[sectorNo][5]); + bldata[10] = (keyB[sectorNo][0]); + bldata[11] = (keyB[sectorNo][1]); + bldata[12] = (keyB[sectorNo][2]); + bldata[13] = (keyB[sectorNo][3]); + bldata[14] = (keyB[sectorNo][4]); + bldata[15] = (keyB[sectorNo][5]); + } + + PrintAndLogEx(NORMAL, "Writing to block %3d: %s", FirstBlockOfSector(sectorNo) + blockNo, sprint_hex(bldata, 16)); + + memcpy(c.d.asBytes + 10, bldata, 16); + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK,&resp,1500)) { + uint8_t isOK = resp.arg[0] & 0xff; + PrintAndLogEx(SUCCESS, "isOk:%02x", isOK); + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + } + } + + fclose(fdump); + return 0; +} + +int CmdHF14AMfNested(const char *Cmd) { + int i, res, iterations; + sector_t *e_sector = NULL; + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t trgBlockNo = 0; + uint8_t trgKeyType = 0; + uint8_t SectorsCnt = 0; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t keyBlock[(MIFARE_DEFAULTKEYS_SIZE + 1) *6]; + uint64_t key64 = 0; + bool transferToEml = false; + bool createDumpFile = false; + FILE *fkeys; + uint8_t standart[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + char *fptr; + + if (strlen(Cmd) < 3) return usage_hf14_nested(); + + char cmdp, ctmp; + cmdp = tolower(param_getchar(Cmd, 0)); + blockNo = param_get8(Cmd, 1); + ctmp = tolower(param_getchar(Cmd, 2)); + + if (ctmp != 'a' && ctmp != 'b') { + PrintAndLogEx(WARNING, "key type must be A or B"); + return 1; + } + + if (ctmp != 'a') + keyType = 1; + + if (param_gethex(Cmd, 3, key, 12)) { + PrintAndLogEx(WARNING, "key must include 12 HEX symbols"); + return 1; + } + + if (cmdp == 'o') { + trgBlockNo = param_get8(Cmd, 4); + ctmp = tolower(param_getchar(Cmd, 5)); + if (ctmp != 'a' && ctmp != 'b') { + PrintAndLogEx(WARNING, "target key type must be A or B"); + return 1; + } + if (ctmp != 'a') { + trgKeyType = 1; + } + } else { + SectorsCnt = NumOfSectors(cmdp); + } + + uint8_t j = 4; + while ( ctmp != 0x00 ) { + + ctmp = tolower(param_getchar(Cmd, j)); + transferToEml |= (ctmp == 't'); + createDumpFile |= (ctmp == 'd'); + + j++; + } + + // check if we can authenticate to sector + res = mfCheckKeys(blockNo, keyType, true, 1, key, &key64); + if (res) { + PrintAndLogEx(WARNING, "Wrong key. Can't authenticate to block:%3d key type:%c", blockNo, keyType ? 'B' : 'A'); + return 3; + } + + if (cmdp == 'o') { + int16_t isOK = mfnested(blockNo, keyType, key, trgBlockNo, trgKeyType, keyBlock, true); + switch (isOK) { + case -1 : PrintAndLogEx(WARNING, "Error: No response from Proxmark.\n"); break; + case -2 : PrintAndLogEx(WARNING, "Button pressed. Aborted.\n"); break; + case -3 : PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break; + case -4 : PrintAndLogEx(FAILED, "No valid key found"); break; + case -5 : + key64 = bytes_to_num(keyBlock, 6); + + // transfer key to the emulator + if (transferToEml) { + uint8_t sectortrailer; + if (trgBlockNo < 32*4) { // 4 block sector + sectortrailer = trgBlockNo | 0x03; + } else { // 16 block sector + sectortrailer = trgBlockNo | 0x0f; + } + mfEmlGetMem(keyBlock, sectortrailer, 1); + + if (!trgKeyType) + num_to_bytes(key64, 6, keyBlock); + else + num_to_bytes(key64, 6, &keyBlock[10]); + mfEmlSetMem(keyBlock, sectortrailer, 1); + PrintAndLogEx(SUCCESS, "Key transferred to emulator memory."); + } + return 0; + default : PrintAndLogEx(WARNING, "Unknown Error.\n"); + } + return 2; + } + else { // ------------------------------------ multiple sectors working + uint64_t t1 = msclock(); + + e_sector = calloc(SectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) return 1; + + //test current key and additional standard keys first + // add parameter key + memcpy( keyBlock + (MIFARE_DEFAULTKEYS_SIZE * 6), key, 6 ); + + for (int cnt = 0; cnt < MIFARE_DEFAULTKEYS_SIZE; cnt++){ + num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t*)(keyBlock + cnt * 6)); + } + + PrintAndLogEx(SUCCESS, "Testing known keys. Sector count=%d", SectorsCnt); + res = mfCheckKeys_fast( SectorsCnt, true, true, 1, MIFARE_DEFAULTKEYS_SIZE + 1, keyBlock, e_sector, false); + + uint64_t t2 = msclock() - t1; + PrintAndLogEx(SUCCESS, "Time to check %d known keys: %.0f seconds\n", MIFARE_DEFAULTKEYS_SIZE, (float)t2/1000.0 ); + PrintAndLogEx(SUCCESS, "enter nested attack"); + + // nested sectors + iterations = 0; + bool calibrate = true; + + for (i = 0; i < MIFARE_SECTOR_RETRY; i++) { + for (uint8_t sectorNo = 0; sectorNo < SectorsCnt; ++sectorNo) { + for (trgKeyType = 0; trgKeyType < 2; ++trgKeyType) { + + if (e_sector[sectorNo].foundKey[trgKeyType]) continue; + + int16_t isOK = mfnested(blockNo, keyType, key, FirstBlockOfSector(sectorNo), trgKeyType, keyBlock, calibrate); + switch (isOK) { + case -1 : PrintAndLogEx(WARNING, "error: No response from Proxmark.\n"); break; + case -2 : PrintAndLogEx(WARNING, "button pressed. Aborted.\n"); break; + case -3 : PrintAndLogEx(FAILED, "Tag isn't vulnerable to Nested Attack (PRNG is not predictable).\n"); break; + case -4 : //key not found + calibrate = false; + iterations++; + continue; + case -5 : + calibrate = false; + iterations++; + e_sector[sectorNo].foundKey[trgKeyType] = 1; + e_sector[sectorNo].Key[trgKeyType] = bytes_to_num(keyBlock, 6); + + res = mfCheckKeys_fast( SectorsCnt, true, true, 2, 1, keyBlock, e_sector, false); + continue; + + default : PrintAndLogEx(WARNING, "unknown Error.\n"); + } + free(e_sector); + return 2; + } + } + } + + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "time in nested: %.0f seconds\n", (float)t1/1000.0); + + + // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? + PrintAndLogEx(INFO, "trying to read key B..."); + for (i = 0; i < SectorsCnt; i++) { + // KEY A but not KEY B + if ( e_sector[i].foundKey[0] && !e_sector[i].foundKey[1] ) { + + uint8_t sectrail = (FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); + + PrintAndLogEx(SUCCESS, "reading block %d", sectrail); + + UsbCommand c = {CMD_MIFARE_READBL, {sectrail, 0, 0}}; + num_to_bytes(e_sector[i].Key[0], 6, c.d.asBytes); // KEY A + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK,&resp,1500)) continue; + + uint8_t isOK = resp.arg[0] & 0xff; + if (!isOK) continue; + + uint8_t *data = resp.d.asBytes; + key64 = bytes_to_num(data+10, 6); + if (key64) { + PrintAndLogEx(SUCCESS, "data: %s", sprint_hex(data+10, 6)); + e_sector[i].foundKey[1] = true; + e_sector[i].Key[1] = key64; + } + } + } + + + //print them + printKeyTable( SectorsCnt, e_sector ); + + // transfer them to the emulator + if (transferToEml) { + for (i = 0; i < SectorsCnt; i++) { + mfEmlGetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + if (e_sector[i].foundKey[0]) + num_to_bytes(e_sector[i].Key[0], 6, keyBlock); + if (e_sector[i].foundKey[1]) + num_to_bytes(e_sector[i].Key[1], 6, &keyBlock[10]); + mfEmlSetMem(keyBlock, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + } + PrintAndLogEx(SUCCESS, "keys transferred to emulator memory."); + } + + // Create dump file + if (createDumpFile) { + fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) { + free(e_sector); + return 1; + } + + if ((fkeys = fopen(fptr, "wb")) == NULL) { + PrintAndLogEx(WARNING, "could not create file " _YELLOW_(%s), fptr); + free(e_sector); + return 1; + } + + PrintAndLogEx(SUCCESS, "saving keys to binary file " _YELLOW_(%s), fptr); + for (i=0; i= FILE_PATH_SIZE ) { + PrintAndLogEx(FAILED, "Filename too long"); + continue; + } + + f = fopen( filename, "r"); + if ( !f ){ + PrintAndLogEx(FAILED, "File: " _YELLOW_(%s) ": not found or locked.", filename); + continue; + } + + // read file + while( fgets(buf, sizeof(buf), f) ){ + if (strlen(buf) < 12 || buf[11] == '\n') + continue; + + while (fgetc(f) != '\n' && !feof(f)) ; //goto next line + + if( buf[0]=='#' ) continue; //The line start with # is comment, skip + + if (!isxdigit(buf[0])){ + PrintAndLogEx(FAILED, "File content error. '" _YELLOW_(%s)"' must include 12 HEX symbols", buf); + continue; + } + + buf[12] = 0; + if ( keyitems - keycnt < 2) { + p = realloc(keyBlock, 6 * (keyitems += 64)); + if (!p) { + PrintAndLogEx(FAILED, "Cannot allocate memory for default keys"); + free(keyBlock); + fclose(f); + return 2; + } + keyBlock = p; + } + int pos = 6 * keycnt; + memset(keyBlock + pos, 0, 6); + num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + pos); + keycnt++; + memset(buf, 0, sizeof(buf)); + } + fclose(f); + PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_(%s), keycnt, filename); + } + } + + if (keycnt == 0 && !use_flashmemory) { + PrintAndLogEx(SUCCESS, "No key specified, trying default keys"); + for (;keycnt < MIFARE_DEFAULTKEYS_SIZE; keycnt++) + PrintAndLogEx(NORMAL, "[%2d] %02x%02x%02x%02x%02x%02x", keycnt, + (keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], + (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5]); + } + + // // initialize storage for found keys + e_sector = calloc(sectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) { + free(keyBlock); + return 1; + } + + uint32_t chunksize = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; + bool firstChunk = true, lastChunk = false; + + // time + uint64_t t1 = msclock(); + + if ( use_flashmemory ) { + PrintAndLogEx(SUCCESS, "Using dictionary in flash memory"); + mfCheckKeys_fast( sectorsCnt, true, true, 1, 0, keyBlock, e_sector, use_flashmemory); + } else { + + // strategys. 1= deep first on sector 0 AB, 2= width first on all sectors + for (uint8_t strategy = 1; strategy < 3; strategy++) { + PrintAndLogEx(SUCCESS, "Running strategy %u", strategy); + + // main keychunk loop + for (uint32_t i = 0; i < keycnt; i += chunksize) { + + if (ukbhit()) { + int gc = getchar(); (void)gc; + PrintAndLogEx(WARNING, "\naborted via keyboard!\n"); + goto out; + } + + uint32_t size = ((keycnt - i) > chunksize) ? chunksize : keycnt - i; + + // last chunk? + if ( size == keycnt - i) + lastChunk = true; + + int res = mfCheckKeys_fast( sectorsCnt, firstChunk, lastChunk, strategy, size, keyBlock + (i * 6), e_sector, false); + + if ( firstChunk ) + firstChunk = false; + + // all keys, aborted + if ( res == 0 || res == 2 ) + goto out; + } // end chunks of keys + firstChunk = true; + lastChunk = false; + } // end strategy + } +out: + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "Time in checkkeys (fast): %.1fs\n", (float)(t1/1000.0)); + + // check.. + uint8_t found_keys = 0; + for (uint8_t i = 0; i < sectorsCnt; ++i) { + + if ( e_sector[i].foundKey[0] ) + found_keys++; + + if ( e_sector[i].foundKey[1] ) + found_keys++; + } + + if ( found_keys == 0 ) { + PrintAndLogEx(WARNING, "No keys found"); + } else { + + printKeyTable( sectorsCnt, e_sector ); + + if (transferToEml) { + uint8_t block[16] = {0x00}; + for (uint8_t i = 0; i < sectorsCnt; ++i ) { + mfEmlGetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + if (e_sector[i].foundKey[0]) + num_to_bytes(e_sector[i].Key[0], 6, block); + if (e_sector[i].foundKey[1]) + num_to_bytes(e_sector[i].Key[1], 6, block+10); + mfEmlSetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + } + PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); + } + + if (createDumpFile) { + fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) + return 1; + + FILE *fkeys = fopen(fptr, "wb"); + if (fkeys == NULL) { + PrintAndLogEx(WARNING, "Could not create file " _YELLOW_(%s), fptr); + free(keyBlock); + free(e_sector); + return 1; + } + PrintAndLogEx(SUCCESS, "Printing keys to binary file " _YELLOW_(%s)"...", fptr); + + for (i=0; i 0xffffffffffff has been inserted for unknown keys.", fptr); + } + } + + free(keyBlock); + free(e_sector); + PrintAndLogEx(NORMAL, ""); + return 0; +} + +int CmdHF14AMfChk(const char *Cmd) { + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 3 || ctmp == 'h') return usage_hf14_chk(); + + FILE * f; + char filename[FILE_PATH_SIZE] = {0}; + char buf[13]; + uint8_t *keyBlock = NULL, *p; + sector_t *e_sector = NULL; + + uint8_t blockNo = 0; + uint8_t SectorsCnt = 1; + uint8_t keyType = 0; + uint32_t keyitems = MIFARE_DEFAULTKEYS_SIZE; + uint64_t key64 = 0; + uint8_t tempkey[6] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + char *fptr; + int clen = 0; + int transferToEml = 0; + int createDumpFile = 0; + int i, res, keycnt = 0; + + keyBlock = calloc(MIFARE_DEFAULTKEYS_SIZE, 6); + if (keyBlock == NULL) return 1; + + for (int cnt = 0; cnt < MIFARE_DEFAULTKEYS_SIZE; cnt++) + num_to_bytes(g_mifare_default_keys[cnt], 6, (uint8_t*)(keyBlock + cnt * 6)); + + if (param_getchar(Cmd, 0)=='*') { + blockNo = 3; + SectorsCnt = NumOfSectors( param_getchar(Cmd+1, 0) ); + } else { + blockNo = param_get8(Cmd, 0); + } + + ctmp = tolower(param_getchar(Cmd, 1)); + clen = param_getlength(Cmd, 1); + if (clen == 1) { + switch (ctmp) { + case 'a': + keyType = 0; + break; + case 'b': + keyType = 1; + break; + case '?': + keyType = 2; + break; + default: + PrintAndLogEx(FAILED, "Key type must be A , B or ?"); + free(keyBlock); + return 1; + }; + } + + for (i = 2; param_getchar(Cmd, i); i++) { + + ctmp = tolower(param_getchar(Cmd, i)); + clen = param_getlength(Cmd, i); + + if (clen == 12) { + + if ( param_gethex(Cmd, i, keyBlock + 6 * keycnt, 12) ){ + PrintAndLogEx(FAILED, "not hex, skipping"); + continue; + } + + if ( keyitems - keycnt < 2) { + p = realloc(keyBlock, 6 * (keyitems += 64)); + if (!p) { + PrintAndLogEx(FAILED, "cannot allocate memory for Keys"); + free(keyBlock); + return 2; + } + keyBlock = p; + } + PrintAndLogEx(NORMAL, "[%2d] key %s", keycnt, sprint_hex( (keyBlock + 6*keycnt), 6 ) );; + keycnt++; + } else if ( clen == 1 ) { + if (ctmp == 't' ) { transferToEml = 1; continue; } + if (ctmp == 'd' ) { createDumpFile = 1; continue; } + } else { + // May be a dic file + if ( param_getstr(Cmd, i, filename, sizeof(filename)) >= FILE_PATH_SIZE ) { + PrintAndLogEx(FAILED, "File name too long"); + continue; + } + + f = fopen( filename , "r"); + if ( !f ) { + PrintAndLogEx(FAILED, "File: " _YELLOW_(%s) ": not found or locked.", filename); + continue; + } + + // load keys from dictionary file + while( fgets(buf, sizeof(buf), f) ){ + if (strlen(buf) < 12 || buf[11] == '\n') + continue; + + while (fgetc(f) != '\n' && !feof(f)) ; //goto next line + + if( buf[0]=='#' ) continue; //The line start with # is comment, skip + + // codesmell, only checks first char? + if (!isxdigit(buf[0])){ + PrintAndLogEx(FAILED, "File content error. '" _YELLOW_(%s)"' must include 12 HEX symbols",buf); + continue; + } + + buf[12] = 0; + + if ( keyitems - keycnt < 2) { + p = realloc(keyBlock, 6 * (keyitems += 64)); + if (!p) { + PrintAndLogEx(FAILED, "Cannot allocate memory for defKeys"); + free(keyBlock); + fclose(f); + return 2; + } + keyBlock = p; + } + memset(keyBlock + 6 * keycnt, 0, 6); + num_to_bytes(strtoll(buf, NULL, 16), 6, keyBlock + 6*keycnt); + //PrintAndLogEx(NORMAL, "check key[%2d] %012" PRIx64, keycnt, bytes_to_num(keyBlock + 6*keycnt, 6)); + keycnt++; + memset(buf, 0, sizeof(buf)); + } + fclose(f); + PrintAndLogEx(SUCCESS, "Loaded %2d keys from " _YELLOW_(%s), keycnt, filename); + } + } + + if (keycnt == 0) { + PrintAndLogEx(INFO, "No key specified, trying default keys"); + for (;keycnt < MIFARE_DEFAULTKEYS_SIZE; keycnt++) + PrintAndLogEx(NORMAL, "[%2d] %02x%02x%02x%02x%02x%02x", keycnt, + (keyBlock + 6*keycnt)[0],(keyBlock + 6*keycnt)[1], (keyBlock + 6*keycnt)[2], + (keyBlock + 6*keycnt)[3], (keyBlock + 6*keycnt)[4], (keyBlock + 6*keycnt)[5], 6); + } + + // initialize storage for found keys + e_sector = calloc(SectorsCnt, sizeof(sector_t)); + if (e_sector == NULL) { + free(keyBlock); + return 1; + } + + // empty e_sector + for(int i = 0; i < SectorsCnt; ++i){ + e_sector[i].Key[0] = 0xffffffffffff; + e_sector[i].Key[1] = 0xffffffffffff; + e_sector[i].foundKey[0] = false; + e_sector[i].foundKey[1] = false; + } + + + uint8_t trgKeyType = 0; + uint32_t max_keys = keycnt > (USB_CMD_DATA_SIZE/6) ? (USB_CMD_DATA_SIZE/6) : keycnt; + + // time + uint64_t t1 = msclock(); + + + // check keys. + for (trgKeyType = (keyType==2)?0:keyType; trgKeyType < 2; (keyType==2) ? (++trgKeyType) : (trgKeyType=2) ) { + + int b = blockNo; + for (int i = 0; i < SectorsCnt; ++i) { + + // skip already found keys. + if (e_sector[i].foundKey[trgKeyType]) continue; + + for (uint32_t c = 0; c < keycnt; c += max_keys) { + + printf("."); fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + PrintAndLogEx(INFO, "\naborted via keyboard!\n"); + goto out; + } + + uint32_t size = keycnt-c > max_keys ? max_keys : keycnt-c; + + res = mfCheckKeys(b, trgKeyType, true, size, &keyBlock[6*c], &key64); + if (!res) { + e_sector[i].Key[trgKeyType] = key64; + e_sector[i].foundKey[trgKeyType] = true; + break; + } + + + } + b < 127 ? ( b +=4 ) : ( b += 16 ); + } + } + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\nTime in checkkeys: %.0f seconds\n", (float)t1/1000.0); + + + // 20160116 If Sector A is found, but not Sector B, try just reading it of the tag? + if ( keyType != 1 ) { + PrintAndLogEx(INFO, "testing to read key B..."); + for (i = 0; i < SectorsCnt; i++) { + // KEY A but not KEY B + if ( e_sector[i].foundKey[0] && !e_sector[i].foundKey[1] ) { + + uint8_t sectrail = (FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); + + PrintAndLogEx(NORMAL, "Reading block %d", sectrail); + + UsbCommand c = {CMD_MIFARE_READBL, {sectrail, 0, 0}}; + num_to_bytes(e_sector[i].Key[0], 6, c.d.asBytes); // KEY A + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if ( !WaitForResponseTimeout(CMD_ACK,&resp,1500)) continue; + + uint8_t isOK = resp.arg[0] & 0xff; + if (!isOK) continue; + + uint8_t *data = resp.d.asBytes; + key64 = bytes_to_num(data+10, 6); + if (key64) { + PrintAndLogEx(NORMAL, "Data:%s", sprint_hex(data+10, 6)); + e_sector[i].foundKey[1] = 1; + e_sector[i].Key[1] = key64; + } + } + } + } + +out: + + //print keys + printKeyTable( SectorsCnt, e_sector ); + + if (transferToEml) { + uint8_t block[16] = {0x00}; + for (uint8_t i = 0; i < SectorsCnt; ++i ) { + mfEmlGetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + if (e_sector[i].foundKey[0]) + num_to_bytes(e_sector[i].Key[0], 6, block); + if (e_sector[i].foundKey[1]) + num_to_bytes(e_sector[i].Key[1], 6, block+10); + mfEmlSetMem(block, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1); + } + PrintAndLogEx(SUCCESS, "Found keys have been transferred to the emulator memory"); + } + + if (createDumpFile) { + fptr = GenerateFilename("hf-mf-", "-key.bin"); + if (fptr == NULL) { + free(keyBlock); + free(e_sector); + return 1; + } + + FILE *fkeys = fopen(fptr, "wb"); + if (fkeys == NULL) { + PrintAndLogEx(WARNING, "Could not create file " _YELLOW_(%s), fptr); + free(keyBlock); + free(e_sector); + return 1; + } + PrintAndLogEx(INFO, "Printing keys to binary file " _YELLOW_(%s)"...", fptr); + + for( i=0; i>1) + , exitAfterNReads + , flags + , flags); + + UsbCommand c = {CMD_SIMULATE_MIFARE_CARD, {flags, exitAfterNReads, 0}}; + memcpy(c.d.asBytes, uid, sizeof(uid)); + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + if(flags & FLAG_INTERACTIVE) { + PrintAndLogEx(INFO, "Press pm3-button or send another cmd to abort simulation"); + + while( !ukbhit() ){ + if (!WaitForResponseTimeout(CMD_ACK, &resp, 1500) ) continue; + if ( !(flags & FLAG_NR_AR_ATTACK) ) break; + if ( (resp.arg[0] & 0xffff) != CMD_SIMULATE_MIFARE_CARD ) break; + + memcpy(data, resp.d.asBytes, sizeof(data)); + readerAttack(data[0], setEmulatorMem, verbose); + } + showSectorTable(); + } + return 0; +} + +int CmdHF14AMfSniff(const char *Cmd){ + bool wantLogToFile = false; + bool wantDecrypt = false; + //bool wantSaveToEml = false; TODO + bool wantSaveToEmlFile = false; + + //var + int res = 0, len = 0, blockLen = 0; + int pckNum = 0, num = 0; + uint8_t sak = 0; + uint8_t uid[10]; + uint8_t uid_len = 0; + uint8_t atqa[2] = {0x00, 0x00}; + bool isTag = false; + uint8_t *buf = NULL; + uint16_t bufsize = 0; + uint8_t *bufPtr = NULL; + uint16_t traceLen = 0; + + memset(uid, 0x00, sizeof(uid)); + + char ctmp = tolower(param_getchar(Cmd, 0)); + if ( ctmp == 'h') return usage_hf14_sniff(); + + for (int i = 0; i < 4; i++) { + ctmp = tolower(param_getchar(Cmd, i)); + if (ctmp == 'l') wantLogToFile = true; + if (ctmp == 'd') wantDecrypt = true; + //if (ctmp == 'e') wantSaveToEml = true; TODO + if (ctmp == 'f') wantSaveToEmlFile = true; + } + + PrintAndLogEx(NORMAL, "-------------------------------------------------------------------------\n"); + PrintAndLogEx(NORMAL, "Executing mifare sniffing command. \n"); + PrintAndLogEx(NORMAL, "Press the key on the proxmark3 device to abort both proxmark3 and client.\n"); + PrintAndLogEx(NORMAL, "Press the key on pc keyboard to abort the client.\n"); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------------------\n"); + + UsbCommand c = {CMD_MIFARE_SNIFFER, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + + // wait cycle + while (true) { + printf("."); fflush(stdout); + if (ukbhit()) { + int gc = getchar(); (void)gc; + PrintAndLogEx(INFO, "\naborted via keyboard!\n"); + break; + } + + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + continue; + } + + res = resp.arg[0] & 0xff; + traceLen = resp.arg[1]; + len = resp.arg[2]; + + if (res == 0) { + PrintAndLogEx(SUCCESS, "hf mifare sniff finished"); + free(buf); + return 0; + } + + if (res == 1) { // there is (more) data to be transferred + if (pckNum == 0) { // first packet, (re)allocate necessary buffer + if (traceLen > bufsize || buf == NULL) { + uint8_t *p; + if (buf == NULL) // not yet allocated + p = calloc(traceLen, sizeof(uint8_t)); + else // need more memory + p = realloc(buf, traceLen); + + if (p == NULL) { + PrintAndLogEx(FAILED, "Cannot allocate memory for trace"); + free(buf); + return 2; + } + buf = p; + } + bufPtr = buf; + bufsize = traceLen; + memset(buf, 0x00, traceLen); + } + + // what happens if LEN is bigger then TRACELEN --iceman + memcpy(bufPtr, resp.d.asBytes, len); + bufPtr += len; + pckNum++; + } + + if (res == 2) { // received all data, start displaying + blockLen = bufPtr - buf; + bufPtr = buf; + PrintAndLogEx(NORMAL, ">\n"); + PrintAndLogEx(SUCCESS, "received trace len: %d packages: %d", blockLen, pckNum); + while (bufPtr - buf < blockLen) { + bufPtr += 6; // skip (void) timing information + len = *((uint16_t *)bufPtr); + if(len & 0x8000) { + isTag = true; + len &= 0x7fff; + } else { + isTag = false; + } + bufPtr += 2; + + // the uid identification package + // 0xFF 0xFF xx xx xx xx xx xx xx xx xx xx aa aa cc 0xFF 0xFF + // x = uid, a = atqa, c = sak + if ((len == 17) && (bufPtr[0] == 0xff) && (bufPtr[1] == 0xff) && (bufPtr[15] == 0xff) && (bufPtr[16] == 0xff)) { + memcpy(uid, bufPtr + 2, 10); + memcpy(atqa, bufPtr + 2 + 10, 2); + switch (atqa[0] & 0xC0) { + case 0x80: uid_len = 10; break; + case 0x40: uid_len = 7; break; + default: uid_len = 4; break; + } + sak = bufPtr[14]; + PrintAndLogEx(SUCCESS, "UID %s | ATQA %02x %02x | SAK 0x%02x", + sprint_hex(uid, uid_len), + atqa[1], + atqa[0], + sak); + if (wantLogToFile || wantDecrypt) { + FillFileNameByUID(logHexFileName, uid, ".log", uid_len); + AddLogCurrentDT(logHexFileName); + PrintAndLogEx(SUCCESS, "Trace saved to %s", logHexFileName); + } + if (wantDecrypt) + mfTraceInit(uid, uid_len, atqa, sak, wantSaveToEmlFile); + } else { + PrintAndLogEx(NORMAL, "%03d| %s |%s", num, isTag ? "TAG" : "RDR", sprint_hex(bufPtr, len)); + if (wantLogToFile) + AddLogHex(logHexFileName, isTag ? "TAG| ":"RDR| ", bufPtr, len); + if (wantDecrypt) + mfTraceDecode(bufPtr, len, wantSaveToEmlFile); + num++; + } + bufPtr += len; + bufPtr += ((len-1)/8+1); // ignore parity + } + pckNum = 0; + } + } // while (true) + + free(buf); + return 0; +} + +int CmdHF14AMfDbg(const char *Cmd) { + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h' ) return usage_hf14_dbg(); + + uint8_t dbgMode = param_get8ex(Cmd, 0, 0, 10); + if (dbgMode > 4) return usage_hf14_dbg(); + + UsbCommand c = {CMD_MIFARE_SET_DBGMODE, {dbgMode, 0, 0}}; + SendCommand(&c); + return 0; +} + +int CmdHF14AMfKeyBrute(const char *Cmd) { + + uint8_t blockNo = 0, keytype = 0; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint64_t foundkey = 0; + + char cmdp = tolower(param_getchar(Cmd, 0)); + if ( cmdp == 'h' ) return usage_hf14_keybrute(); + + // block number + blockNo = param_get8(Cmd, 0); + + // keytype + cmdp = tolower(param_getchar(Cmd, 1)); + if ( cmdp == 'b' ) keytype = 1; + + // key + if (param_gethex(Cmd, 2, key, 12)) return usage_hf14_keybrute(); + + uint64_t t1 = msclock(); + + if (mfKeyBrute( blockNo, keytype, key, &foundkey)) + PrintAndLogEx(SUCCESS, "found valid key: %012" PRIx64 " \n", foundkey); + else + PrintAndLogEx(FAILED, "key not found"); + + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\ntime in keybrute: %.0f seconds\n", (float)t1/1000.0); + return 0; +} + +void printKeyTable( uint8_t sectorscnt, sector_t *e_sector ){ + char strA[12+1] = {0}; + char strB[12+1] = {0}; + PrintAndLogEx(NORMAL, "|---|----------------|---|----------------|---|"); + PrintAndLogEx(NORMAL, "|sec|key A |res|key B |res|"); + PrintAndLogEx(NORMAL, "|---|----------------|---|----------------|---|"); + for (uint8_t i = 0; i < sectorscnt; ++i) { + + snprintf(strA, sizeof(strA), "------------"); + snprintf(strB, sizeof(strB), "------------"); + + if ( e_sector[i].foundKey[0] ) + snprintf(strA, sizeof(strA), "%012" PRIx64, e_sector[i].Key[0]); + + if ( e_sector[i].foundKey[1] ) + snprintf(strB, sizeof(strB), "%012" PRIx64, e_sector[i].Key[1]); + + + PrintAndLogEx(NORMAL, "|%03d| %s | %d | %s | %d |" + , i + , strA, e_sector[i].foundKey[0] + , strB, e_sector[i].foundKey[1] + ); + } + PrintAndLogEx(NORMAL, "|---|----------------|---|----------------|---|"); +} + +// EMULATOR COMMANDS +int CmdHF14AMfEGet(const char *Cmd) { + uint8_t blockNo = 0; + uint8_t data[16] = {0x00}; + char c = tolower(param_getchar(Cmd, 0)); + + if (strlen(Cmd) < 1 || c == 'h') return usage_hf14_eget(); + + blockNo = param_get8(Cmd, 0); + + PrintAndLogEx(NORMAL, ""); + if (!mfEmlGetMem(data, blockNo, 1)) { + PrintAndLogEx(NORMAL, "data[%3d]:%s", blockNo, sprint_hex(data, sizeof(data))); + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + return 0; +} + +int CmdHF14AMfEClear(const char *Cmd) { + char c = tolower(param_getchar(Cmd, 0)); + if (c == 'h') return usage_hf14_eclr(); + + UsbCommand cmd = {CMD_MIFARE_EML_MEMCLR, {0, 0, 0}}; + clearCommandBuffer(); + SendCommand(&cmd); + return 0; +} + +int CmdHF14AMfESet(const char *Cmd) { + char c = tolower(param_getchar(Cmd, 0)); + uint8_t memBlock[16]; + uint8_t blockNo = 0; + memset(memBlock, 0x00, sizeof(memBlock)); + + if (strlen(Cmd) < 3 || c == 'h') + return usage_hf14_eset(); + + blockNo = param_get8(Cmd, 0); + + if (param_gethex(Cmd, 1, memBlock, 32)) { + PrintAndLogEx(WARNING, "block data must include 32 HEX symbols"); + return 1; + } + + // 1 - blocks count + return mfEmlSetMem(memBlock, blockNo, 1); +} + +int CmdHF14AMfELoad(const char *Cmd) { + + size_t counter = 0; + char filename[FILE_PATH_SIZE]; + int blockNum, numBlocks, nameParamNo = 1; + uint8_t blockWidth = 16; + char c = tolower(param_getchar(Cmd, 0)); + + if ( strlen(Cmd) < 2 && c == 'h' ) + return usage_hf14_eload(); + + switch (c) { + case '0' : numBlocks = MIFARE_MINI_MAXBLOCK; break; + case '1' : + case '\0': numBlocks = MIFARE_1K_MAXBLOCK; break; + case '2' : numBlocks = MIFARE_2K_MAXBLOCK; break; + case '4' : numBlocks = MIFARE_4K_MAXBLOCK; break; + case 'u' : numBlocks = 255; blockWidth = 4; break; + default: { + numBlocks = MIFARE_1K_MAXBLOCK; + nameParamNo = 0; + } + } + uint32_t numblk2 = param_get32ex(Cmd, 2, 0, 10); + if (numblk2 > 0) + numBlocks = numblk2; + + param_getstr(Cmd, nameParamNo, filename, sizeof(filename)); + + uint8_t *data = calloc(4096, sizeof(uint8_t)); + size_t datalen = 0; + //int res = loadFile(filename, "bin", data, &datalen); + int res = loadFileEML( filename, "eml", data, &datalen); + if ( res ) { + free(data); + return 1; + } + + // 64 or 256 blocks. + if ( (datalen % blockWidth) != 0 ) { + PrintAndLogEx(FAILED, "File content error. Size doesn't match blockwidth "); + free(data); + return 2; + } + + PrintAndLogEx(INFO, "Copying to emulator memory"); + + blockNum = 0; + while ( datalen ) { + + if (mfEmlSetMem_xt(data + counter, blockNum, 1, blockWidth)) { + PrintAndLogEx(FAILED, "Cant set emul block: %3d", blockNum); + free(data); + return 3; + } + printf("."); fflush(stdout); + + blockNum++; + counter += blockWidth; + datalen -= blockWidth; + } + PrintAndLogEx(NORMAL, "\n"); + + // Ultralight /Ntag + if ( blockWidth == 4 ) { + if ((blockNum != numBlocks)) { + PrintAndLogEx(FAILED, "Warning, Ultralight/Ntag file content, Loaded %d blocks into emulator memory", blockNum); + free(data); + return 0; + } + } else { + if ((blockNum != numBlocks)) { + PrintAndLogEx(FAILED, "Error, file content, Only loaded %d blocks, must be %d blocks into emulator memory", blockNum, numBlocks); + free(data); + return 4; + } + } + PrintAndLogEx(SUCCESS, "Loaded %d blocks from file: " _YELLOW_(%s), blockNum, filename); + free(data); + return 0; +} + +int CmdHF14AMfESave(const char *Cmd) { + + char filename[FILE_PATH_SIZE]; + char * fnameptr = filename; + uint8_t *dump; + int len, bytes, nameParamNo = 1; + uint16_t blocks; + + memset(filename, 0, sizeof(filename)); + + char c = tolower(param_getchar(Cmd, 0)); + if (c == 'h') return usage_hf14_esave(); + + blocks = NumOfBlocks(c); + bytes = blocks * MFBLOCK_SIZE; + + dump = calloc(bytes, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return 1; + } + memset(dump, 0, bytes); + + PrintAndLogEx(INFO, "downloading from emulator memory"); + if (!GetFromDevice( BIG_BUF_EML, dump, bytes, 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "Fail, transfer from device time-out"); + free(dump); + return 2; + } + + len = param_getstr(Cmd, nameParamNo, filename, sizeof(filename)); + if (len > FILE_PATH_SIZE - 5) len = FILE_PATH_SIZE - 5; + + // user supplied filename? + if (len < 1) { + fnameptr += sprintf(fnameptr, "hf-mf-"); + FillFileNameByUID(fnameptr, dump, "-dump", 4); + } + + saveFile(filename, "bin", dump, bytes); + saveFileEML(filename, "eml", dump, bytes, MFBLOCK_SIZE); + saveFileJSON(filename, "json", jsfCardMemory, dump, bytes); + free(dump); + return 0; +} + +int CmdHF14AMfECFill(const char *Cmd) { + uint8_t keyType = 0; + uint8_t numSectors = 16; + char c = tolower(param_getchar(Cmd, 0)); + + if (strlen(Cmd) < 1 || c == 'h') + return usage_hf14_ecfill(); + + if (c != 'a' && c != 'b') { + PrintAndLogEx(WARNING, "Key type must be A or B"); + return 1; + } + if (c != 'a') + keyType = 1; + + c = tolower(param_getchar(Cmd, 1)); + numSectors = NumOfSectors(c); + + PrintAndLogEx(NORMAL, "--params: numSectors: %d, keyType: %c\n", numSectors, (keyType == 0) ? 'A' : 'B'); + UsbCommand cmd = {CMD_MIFARE_EML_CARDLOAD, {numSectors, keyType, 0}}; + clearCommandBuffer(); + SendCommand(&cmd); + return 0; +} + +int CmdHF14AMfEKeyPrn(const char *Cmd) { + int i; + uint8_t numSectors; + uint8_t data[16]; + uint64_t keyA, keyB; + + char c = tolower(param_getchar(Cmd, 0)); + if ( c == 'h' ) + return usage_hf14_ekeyprn(); + + numSectors = NumOfSectors(c); + + PrintAndLogEx(NORMAL, "|---|----------------|----------------|"); + PrintAndLogEx(NORMAL, "|sec|key A |key B |"); + PrintAndLogEx(NORMAL, "|---|----------------|----------------|"); + for (i = 0; i < numSectors; i++) { + if (mfEmlGetMem(data, FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1, 1)) { + PrintAndLogEx(WARNING, "error get block %d", FirstBlockOfSector(i) + NumBlocksPerSector(i) - 1); + break; + } + keyA = bytes_to_num(data, 6); + keyB = bytes_to_num(data + 10, 6); + PrintAndLogEx(NORMAL, "|%03d| %012" PRIx64 " | %012" PRIx64 " |", i, keyA, keyB); + } + PrintAndLogEx(NORMAL, "|---|----------------|----------------|"); + return 0; +} + +// CHINESE MAGIC COMMANDS +int CmdHF14AMfCSetUID(const char *Cmd) { + uint8_t wipeCard = 0; + uint8_t uid[8] = {0x00}; + uint8_t oldUid[8] = {0x00}; + uint8_t atqa[2] = {0x00}; + uint8_t sak[1] = {0x00}; + uint8_t atqaPresent = 1; + int res, argi = 0; + char ctmp; + + if (strlen(Cmd) < 1 || param_getchar(Cmd, argi) == 'h') + return usage_hf14_csetuid(); + + if (param_getchar(Cmd, argi) && param_gethex(Cmd, argi, uid, 8)) + return usage_hf14_csetuid(); + + argi++; + + ctmp = tolower(param_getchar(Cmd, argi)); + if (ctmp == 'w') { + wipeCard = 1; + atqaPresent = 0; + } + + if (atqaPresent) { + if (param_getchar(Cmd, argi)) { + if (param_gethex(Cmd, argi, atqa, 4)) { + PrintAndLogEx(WARNING, "ATQA must include 4 HEX symbols"); + return 1; + } + argi++; + if (!param_getchar(Cmd, argi) || param_gethex(Cmd, argi, sak, 2)) { + PrintAndLogEx(WARNING, "SAK must include 2 HEX symbols"); + return 1; + } + argi++; + } else + atqaPresent = 0; + } + + if (!wipeCard) { + ctmp = tolower(param_getchar(Cmd, argi)); + if (ctmp == 'w') { + wipeCard = 1; + } + } + + PrintAndLogEx(NORMAL, "--wipe card:%s uid:%s", (wipeCard)?"YES":"NO", sprint_hex(uid, 4)); + + res = mfCSetUID(uid, (atqaPresent) ? atqa : NULL, (atqaPresent) ? sak : NULL, oldUid, wipeCard); + if (res) { + PrintAndLogEx(WARNING, "Can't set UID. error=%d", res); + return 1; + } + + PrintAndLogEx(SUCCESS, "old UID:%s", sprint_hex(oldUid, 4)); + PrintAndLogEx(SUCCESS, "new UID:%s", sprint_hex(uid, 4)); + return 0; +} + +int CmdHF14AMfCSetBlk(const char *Cmd) { + uint8_t block[16] = {0x00}; + uint8_t blockNo = 0; + uint8_t params = MAGIC_SINGLE; + int res; + char ctmp = tolower(param_getchar(Cmd, 0)); + + if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_csetblk(); + + blockNo = param_get8(Cmd, 0); + + if (param_gethex(Cmd, 1, block, 32)) return usage_hf14_csetblk(); + + ctmp = tolower(param_getchar(Cmd, 2)); + if (ctmp == 'w') + params |= MAGIC_WIPE; + + PrintAndLogEx(NORMAL, "--block number:%2d data:%s", blockNo, sprint_hex(block, 16)); + + res = mfCSetBlock(blockNo, block, NULL, params); + if (res) { + PrintAndLogEx(WARNING, "Can't write block. error=%d", res); + return 1; + } + return 0; +} + +int CmdHF14AMfCLoad(const char *Cmd) { + + uint8_t buf8[16] = {0x00}; + uint8_t fillFromEmulator = 0; + int blockNum, flags = 0; + bool fillFromJson = false; + bool fillFromBin = false; + char fileName[50] = {0}; + + char ctmp = tolower(param_getchar(Cmd, 0)); + if ( param_getlength(Cmd, 0) == 1 ) { + if (ctmp == 'h' || ctmp == 0x00) return usage_hf14_cload(); + if (ctmp == 'e' ) fillFromEmulator = 1; + if (ctmp == 'j' ) fillFromJson = true; + if (ctmp == 'b' ) fillFromBin = true; + } + + if (fillFromJson || fillFromBin) + param_getstr(Cmd, 1, fileName, sizeof(fileName)); + + + if (fillFromEmulator) { + for (blockNum = 0; blockNum < 16 * 4; blockNum += 1) { + if (mfEmlGetMem(buf8, blockNum, 1)) { + PrintAndLogEx(WARNING, "Cant get block: %d", blockNum); + return 2; + } + if (blockNum == 0) flags = MAGIC_INIT + MAGIC_WUPC; // switch on field and send magic sequence + if (blockNum == 1) flags = 0; // just write + if (blockNum == 16 * 4 - 1) flags = MAGIC_HALT + MAGIC_OFF; // Done. Magic Halt and switch off field. + + if (mfCSetBlock(blockNum, buf8, NULL, flags)) { + PrintAndLogEx(WARNING, "Cant set magic card block: %d", blockNum); + return 3; + } + printf("."); fflush(stdout); + } + PrintAndLogEx(NORMAL, "\n"); + return 0; + } + + size_t maxdatalen = 4096; + uint8_t *data = calloc(maxdatalen, sizeof(uint8_t)); + size_t datalen = 0; + int res = 0; + if (fillFromBin) { + res = loadFile(fileName, "bin", data, &datalen); + } else { + if (fillFromJson) { + res = loadFileJSON(fileName, "json", data, maxdatalen, &datalen); + } else { + res = loadFileEML( Cmd, "eml", data, &datalen); + } + } + + if ( res ) { + if ( data ) + free(data); + return 1; + } + +// PrintAndLogEx(INFO, "DATA | %s", sprint_hex(data+1000, 24) ); + + + // 64 or 256blocks. + if (datalen != 1024 && datalen != 4096) { + PrintAndLogEx(WARNING, "File content error. "); + free(data); + return 2; + } + + PrintAndLogEx(INFO, "Copying to magic card"); + + blockNum = 0; + while ( datalen ) { + + // switch on field and send magic sequence + if (blockNum == 0) flags = MAGIC_INIT + MAGIC_WUPC; + + // write + if (blockNum == 1) flags = 0; + + // Switch off field. + if (blockNum == 16 * 4 - 1) flags = MAGIC_HALT + MAGIC_OFF; + + if (mfCSetBlock(blockNum, data + (16 * blockNum), NULL, flags)) { + PrintAndLogEx(WARNING, "Can't set magic card block: %d", blockNum); + free(data); + return 3; + } + + datalen -= 16; + + printf("."); fflush(stdout); + blockNum++; + + // magic card type - mifare 1K + if (blockNum >= MIFARE_1K_MAXBLOCK ) break; + } + PrintAndLogEx(NORMAL, "\n"); + + // 64 or 256blocks. + if (blockNum != 16 * 4 && blockNum != 32 * 4 + 8 * 16){ + PrintAndLogEx(WARNING, "File content error. There must be 64 blocks"); + free(data); + return 4; + } + + PrintAndLogEx(SUCCESS, "Card loaded %d blocks from file", blockNum); + free(data); + return 0; +} + +int CmdHF14AMfCGetBlk(const char *Cmd) { + uint8_t data[16] = {0}; + uint8_t blockNo = 0; + int res; + memset(data, 0x00, sizeof(data)); + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_cgetblk(); + + blockNo = param_get8(Cmd, 0); + + PrintAndLogEx(NORMAL, "--block number:%2d ", blockNo); + + res = mfCGetBlock(blockNo, data, MAGIC_SINGLE); + if (res) { + PrintAndLogEx(WARNING, "Can't read block. error=%d", res); + return 1; + } + + PrintAndLogEx(NORMAL, "data: %s", sprint_hex(data, sizeof(data))); + + if (mfIsSectorTrailer(blockNo)) { + PrintAndLogEx(NORMAL, "Trailer decoded:"); + PrintAndLogEx(NORMAL, "Key A: %s", sprint_hex_inrow(data, 6)); + PrintAndLogEx(NORMAL, "Key B: %s", sprint_hex_inrow(&data[10], 6)); + int bln = mfFirstBlockOfSector(mfSectorNum(blockNo)); + int blinc = (mfNumBlocksPerSector(mfSectorNum(blockNo)) > 4) ? 5 : 1; + for (int i = 0; i < 4; i++) { + PrintAndLogEx(NORMAL, "Access block %d%s: %s", bln, ((blinc > 1) && (i < 3) ? "+" : "") , mfGetAccessConditionsDesc(i, &data[6])); + bln += blinc; + } + PrintAndLogEx(NORMAL, "UserData: %s", sprint_hex_inrow(&data[9], 1)); + } + + return 0; +} + +int CmdHF14AMfCGetSc(const char *Cmd) { + uint8_t data[16] = {0}; + uint8_t sector = 0; + int i, res, flags; + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_cgetsc(); + + sector = param_get8(Cmd, 0); + if (sector > 39) { + PrintAndLogEx(WARNING, "Sector number must be less then 40"); + return 1; + } + + PrintAndLogEx(NORMAL, "\n # | data | Sector | %02d/ 0x%02X ", sector, sector); + PrintAndLogEx(NORMAL, "----+------------------------------------------------"); + uint8_t blocks = 4; + uint8_t start = sector * 4; + if ( sector > 32 ) { + blocks = 16; + start = 128 + ( sector - 32 ) * 16; + } + + flags = MAGIC_INIT + MAGIC_WUPC; + + for (i = 0; i < blocks; i++) { + if (i == 1) flags = 0; + if (i == blocks-1) flags = MAGIC_HALT + MAGIC_OFF; + + res = mfCGetBlock( start + i, data, flags); + if (res) { + PrintAndLogEx(WARNING, "Can't read block. %d error=%d", start + i, res); + return 1; + } + PrintAndLogEx(NORMAL, "%3d | %s", start + i, sprint_hex(data, 16)); + } + return 0; +} + +int CmdHF14AMfCSave(const char *Cmd) { + + char filename[FILE_PATH_SIZE]; + char * fnameptr = filename; + uint8_t *dump; + bool fillEmulator = false; + bool errors = false, hasname = false, useuid = false; + int i, len, flags; + uint8_t numblocks = 0, cmdp = 0; + uint16_t bytes = 0; + char ctmp; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + ctmp = tolower(param_getchar(Cmd, cmdp)); + switch (ctmp) { + case 'e': + useuid = true; + fillEmulator = true; + cmdp++; + break; + case 'h': + return usage_hf14_csave(); + case '0': + case '1': + case '2': + case '4': + numblocks = NumOfBlocks(ctmp); + bytes = numblocks * MFBLOCK_SIZE; + PrintAndLogEx(SUCCESS, "Saving magic mifare %cK", ctmp); + cmdp++; + break; + case 'u': + useuid = true; + hasname = true; + cmdp++; + break; + case 'o': + len = param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE); + if (len < 1) { + errors = true; + break; + } + + useuid = false; + hasname = true; + cmdp += 2; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + if (!hasname && !fillEmulator) errors = true; + + if (errors || cmdp == 0) return usage_hf14_csave(); + + dump = calloc(bytes, sizeof(uint8_t)); + if (!dump) { + PrintAndLogEx(WARNING, "Fail, cannot allocate memory"); + return 1; + } + memset(dump, 0, bytes); + + flags = MAGIC_INIT + MAGIC_WUPC; + for (i = 0; i < numblocks; i++) { + if (i == 1) flags = 0; + if (i == numblocks - 1) flags = MAGIC_HALT + MAGIC_OFF; + + if (mfCGetBlock(i, dump + (i*MFBLOCK_SIZE), flags)) { + PrintAndLogEx(WARNING, "Cant get block: %d", i); + free(dump); + return 2; + } + } + + if ( useuid ){ + fnameptr += sprintf(fnameptr, "hf-mf-"); + FillFileNameByUID(fnameptr, dump, "-dump", 4); + } + + if (fillEmulator) { + PrintAndLogEx(INFO, "uploading to emulator memory"); + for (i = 0; i < numblocks; i += 5) { + if (mfEmlSetMem(dump + (i*MFBLOCK_SIZE), i, 5)) { + PrintAndLogEx(WARNING, "Cant set emul block: %d", i); + } + printf("."); fflush(stdout); + } + PrintAndLogEx(NORMAL, "\n"); + PrintAndLogEx(SUCCESS, "uploaded %d bytes to emulator memory", bytes); + } + + saveFile(filename, "bin", dump, bytes); + saveFileEML(filename, "eml", dump, bytes, MFBLOCK_SIZE); + saveFileJSON(filename, "json", jsfCardMemory, dump, bytes); + free(dump); + return 0; +} + +//needs nt, ar, at, Data to decrypt +int CmdHf14AMfDecryptBytes(const char *Cmd){ + + char ctmp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) < 1 || ctmp == 'h') return usage_hf14_decryptbytes(); + + uint32_t nt = param_get32ex(Cmd,0,0,16); + uint32_t ar_enc = param_get32ex(Cmd,1,0,16); + uint32_t at_enc = param_get32ex(Cmd,2,0,16); + + int len = param_getlength(Cmd, 3); + if (len & 1 ) { + PrintAndLogEx(WARNING, "Uneven hex string length. LEN=%d", len); + return 1; + } + + PrintAndLogEx(NORMAL, "nt\t%08X", nt); + PrintAndLogEx(NORMAL, "ar enc\t%08X", ar_enc); + PrintAndLogEx(NORMAL, "at enc\t%08X", at_enc); + + uint8_t *data = calloc(len, sizeof(uint8_t)); + param_gethex_ex(Cmd, 3, data, &len); + len >>= 1; + tryDecryptWord( nt, ar_enc, at_enc, data, len); + free (data); + return 0; +} + +int CmdHf14AMfSetMod(const char *Cmd) { + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t mod = 2; + + char ctmp = param_getchar(Cmd, 0); + if (ctmp == '0') { + mod = 0; + } else if (ctmp == '1') { + mod = 1; + } + int gethexfail = param_gethex(Cmd, 1, key, 12); + if (mod == 2 || gethexfail) { + PrintAndLogEx(NORMAL, "Sets the load modulation strength of a MIFARE Classic EV1 card."); + PrintAndLogEx(NORMAL, "Usage: hf mf setmod <0|1> "); + PrintAndLogEx(NORMAL, " 0 = normal modulation"); + PrintAndLogEx(NORMAL, " 1 = strong modulation (default)"); + return 1; + } + + UsbCommand c = {CMD_MIFARE_SETMOD, {mod, 0, 0}}; + memcpy(c.d.asBytes, key, 6); + clearCommandBuffer(); + SendCommand(&c); + + UsbCommand resp; + if (WaitForResponseTimeout(CMD_ACK, &resp, 1500)) { + uint8_t ok = resp.arg[0] & 0xff; + PrintAndLogEx(SUCCESS, "isOk:%02x", ok); + if (!ok) + PrintAndLogEx(FAILED, "Failed."); + } else { + PrintAndLogEx(WARNING, "Command execute timeout"); + } + return 0; +} + +// Mifare NACK bug detection +int CmdHf14AMfNack(const char *Cmd) { + + bool verbose = false; + char ctmp = tolower(param_getchar(Cmd, 0)); + if ( ctmp == 'h' ) return usage_hf14_nack(); + if ( ctmp == 'v' ) verbose = true; + + if ( verbose ) + PrintAndLogEx(INFO, "Started testing card for NACK bug. Press key to abort"); + + detect_classic_nackbug(verbose); + return 0; +} + +int CmdHF14AMfice(const char *Cmd) { + + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t trgBlockNo = 0; + uint8_t trgKeyType = 1; + bool slow = false; + bool initialize = true; + bool acquisition_completed = false; + uint8_t cmdp=0; + uint32_t flags = 0; + uint32_t total_num_nonces = 0; + char ctmp; + char filename[FILE_PATH_SIZE], *fptr; + FILE *fnonces = NULL; + UsbCommand resp; + + uint32_t part_limit = 3000; + uint32_t limit = 50000; + + while ((ctmp = param_getchar(Cmd, cmdp))) { + switch(tolower(ctmp)) + { + case 'h': + return usage_hf14_ice(); + case 'f': + param_getstr(Cmd, cmdp+1, filename, FILE_PATH_SIZE); + cmdp++; + break; + case 'l': + limit = param_get32ex(Cmd, cmdp+1, 50000, 10); + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'\n", ctmp); + usage_hf14_ice(); + return 1; + } + cmdp++; + } + + if(filename[0]=='\0') + { + fptr = GenerateFilename("hf-mf-","-nonces.bin"); + if (fptr == NULL) + return 1; + strcpy(filename, fptr); + } + + PrintAndLogEx(NORMAL, "Collecting %u nonces \n", limit); + + if ((fnonces = fopen(filename,"wb")) == NULL) { + PrintAndLogEx(WARNING, "Could not create file " _YELLOW_(%s),filename); + return 3; + } + + clearCommandBuffer(); + + uint64_t t1 = msclock(); + + do { + if (ukbhit()) { + int gc = getchar(); (void)gc; + PrintAndLogEx(INFO, "\naborted via keyboard!\n"); + break; + } + + flags = 0; + flags |= initialize ? 0x0001 : 0; + flags |= slow ? 0x0002 : 0; + UsbCommand c = {CMD_MIFARE_ACQUIRE_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; + clearCommandBuffer(); + SendCommand(&c); + + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) goto out; + if (resp.arg[0]) goto out; + + uint32_t items = resp.arg[2]; + if (fnonces) { + fwrite(resp.d.asBytes, 1, items*4, fnonces); + fflush(fnonces); + } + + total_num_nonces += items; + if ( total_num_nonces > part_limit ) { + PrintAndLogEx(INFO, "Total nonces %u\n", total_num_nonces); + part_limit += 3000; + } + + acquisition_completed = ( total_num_nonces > limit); + + initialize = false; + + } while (!acquisition_completed); + +out: + PrintAndLogEx(SUCCESS, "time: %" PRIu64 " seconds\n", (msclock()-t1)/1000); + + if ( fnonces ) { + fflush(fnonces); + fclose(fnonces); + } + + UsbCommand c = {CMD_MIFARE_ACQUIRE_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, 4}}; + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +int CmdHF14AMfAuth4(const char *Cmd) { + uint8_t keyn[20] = {0}; + int keynlen = 0; + uint8_t key[16] = {0}; + int keylen = 0; + + CLIParserInit("hf mf auth4", + "Executes AES authentication command in ISO14443-4", + "Usage:\n\thf mf auth4 4000 000102030405060708090a0b0c0d0e0f -> executes authentication\n" + "\thf mf auth4 9003 FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF -> executes authentication\n"); + + void* argtable[] = { + arg_param_begin, + arg_str1(NULL, NULL, "", NULL), + arg_str1(NULL, NULL, "", NULL), + arg_param_end + }; + CLIExecWithReturn(Cmd, argtable, true); + + CLIGetHexWithReturn(1, keyn, &keynlen); + CLIGetHexWithReturn(2, key, &keylen); + CLIParserFree(); + + if (keynlen != 2) { + PrintAndLogEx(ERR, " must be 2 bytes long instead of: %d", keynlen); + return 1; + } + + if (keylen != 16) { + PrintAndLogEx(ERR, " must be 16 bytes long instead of: %d", keylen); + return 1; + } + + return MifareAuth4(NULL, keyn, key, true, false, true); +} + +// https://www.nxp.com/docs/en/application-note/AN10787.pdf +int CmdHF14AMfMAD(const char *cmd) { + + CLIParserInit("hf mf mad", + "Checks and prints Mifare Application Directory (MAD)", + "Usage:\n\thf mf mad -> shows MAD if exists\n" + "\thf mf mad -a 03e1 -k ffffffffffff -b -> shows NDEF data if exists. read card with custom key and key B\n"); + + void* argtable[] = { + arg_param_begin, + arg_lit0("vV", "verbose", "show technical data"), + arg_str0("aA", "aid", "print all sectors with aid", NULL), + arg_str0("kK", "key", "key for printing sectors", NULL), + arg_lit0("bB", "keyb", "use key B for access printing sectors (by default: key A)"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + bool verbose = arg_get_lit(1); + uint8_t aid[2] = {0}; + int aidlen; + CLIGetHexWithReturn(2, aid, &aidlen); + uint8_t key[6] = {0}; + int keylen; + CLIGetHexWithReturn(3, key, &keylen); + bool keyB = arg_get_lit(4); + + CLIParserFree(); + + if (aidlen != 2 && keylen > 0) { + PrintAndLogEx(WARNING, "do not need a key without aid."); + } + + uint8_t sector0[16 * 4] = {0}; + uint8_t sector10[16 * 4] = {0}; + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { + PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + if (verbose) { + for(int i = 0; i < 4; i ++) + PrintAndLogEx(NORMAL, "[%d] %s", i, sprint_hex(§or0[i * 16], 16)); + } + + bool haveMAD2 = false; + MAD1DecodeAndPrint(sector0, verbose, &haveMAD2); + + if (haveMAD2) { + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { + PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + MAD2DecodeAndPrint(sector10, verbose); + } + + if (aidlen == 2) { + uint16_t aaid = (aid[0] << 8) + aid[1]; + PrintAndLogEx(NORMAL, "\n-------------- AID 0x%04x ---------------", aaid); + + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + if (MADDecode(sector0, sector10, mad, &madlen)) { + PrintAndLogEx(ERR, "can't decode mad."); + return 10; + } + + uint8_t akey[6] = {0}; + memcpy(akey, g_mifare_ndef_key, 6); + if (keylen == 6) { + memcpy(akey, key, 6); + } + + for (int i = 0; i < madlen; i++) { + if (aaid == mad[i]) { + uint8_t vsector[16 * 4] = {0}; + if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, akey, vsector)) { + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(ERR, "read sector %d error.", i + 1); + return 2; + } + + for(int j = 0; j < (verbose ? 4 : 3); j ++) + PrintAndLogEx(NORMAL, " [%03d] %s", (i + 1) * 4 + j, sprint_hex(&vsector[j * 16], 16)); + } + } + } + + return 0; +} + +int CmdHFMFNDEF(const char *cmd) { + + CLIParserInit("hf mf ndef", + "Prints NFC Data Exchange Format (NDEF)", + "Usage:\n\thf mf ndef -> shows NDEF data\n" + "\thf mf ndef -a 03e1 -k ffffffffffff -b -> shows NDEF data with custom AID, key and with key B\n"); + + void* argtable[] = { + arg_param_begin, + arg_litn("vV", "verbose", 0, 2, "show technical data"), + arg_str0("aA", "aid", "replace default aid for NDEF", NULL), + arg_str0("kK", "key", "replace default key for NDEF", NULL), + arg_lit0("bB", "keyb", "use key B for access sectors (by default: key A)"), + arg_param_end + }; + CLIExecWithReturn(cmd, argtable, true); + + bool verbose = arg_get_lit(1); + bool verbose2 = arg_get_lit(1) > 1; + uint8_t aid[2] = {0}; + int aidlen; + CLIGetHexWithReturn(2, aid, &aidlen); + uint8_t key[6] = {0}; + int keylen; + CLIGetHexWithReturn(3, key, &keylen); + bool keyB = arg_get_lit(4); + + CLIParserFree(); + + uint16_t ndefAID = 0x03e1; + if (aidlen == 2) + ndefAID = (aid[0] << 8) + aid[1]; + + uint8_t ndefkey[6] = {0}; + memcpy(ndefkey, g_mifare_ndef_key, 6); + if (keylen == 6) { + memcpy(ndefkey, key, 6); + } + + uint8_t sector0[16 * 4] = {0}; + uint8_t sector10[16 * 4] = {0}; + uint8_t data[4096] = {0}; + int datalen = 0; + + PrintAndLogEx(NORMAL, ""); + + if (mfReadSector(MF_MAD1_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector0)) { + PrintAndLogEx(ERR, "read sector 0 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + + bool haveMAD2 = false; + int res = MADCheck(sector0, NULL, verbose, &haveMAD2); + if (res) { + PrintAndLogEx(ERR, "MAD error %d.", res); + return res; + } + + if (haveMAD2) { + if (mfReadSector(MF_MAD2_SECTOR, MF_KEY_A, (uint8_t *)g_mifare_mad_key, sector10)) { + PrintAndLogEx(ERR, "read sector 0x10 error. card don't have MAD or don't have MAD on default keys."); + return 2; + } + } + + uint16_t mad[7 + 8 + 8 + 8 + 8] = {0}; + size_t madlen = 0; + if (MADDecode(sector0, (haveMAD2 ? sector10 : NULL), mad, &madlen)) { + PrintAndLogEx(ERR, "can't decode mad."); + return 10; + } + + printf("data reading:"); + for (int i = 0; i < madlen; i++) { + if (ndefAID == mad[i]) { + uint8_t vsector[16 * 4] = {0}; + if (mfReadSector(i + 1, keyB ? MF_KEY_B : MF_KEY_A, ndefkey, vsector)) { + PrintAndLogEx(ERR, "read sector %d error.", i + 1); + return 2; + } + + memcpy(&data[datalen], vsector, 16 * 3); + datalen += 16 * 3; + + printf("."); + } + } + printf(" OK\n"); + + if (!datalen) { + PrintAndLogEx(ERR, "no NDEF data."); + return 11; + } + + if (verbose2) { + PrintAndLogEx(NORMAL, "NDEF data:"); + dump_buffer(data, datalen, stdout, 1); + } + + NDEFDecodeAndPrint(data, datalen, verbose); + + return 0; +} + +int CmdHF14AMfList(const char *Cmd) { + CmdTraceList("mf"); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"list", CmdHF14AMfList, 0, "[Deprecated] List ISO 14443-a / Mifare history"}, + {"darkside", CmdHF14AMfDarkside, 0, "Darkside attack. read parity error messages."}, + {"nested", CmdHF14AMfNested, 0, "Nested attack. Test nested authentication"}, + {"hardnested", CmdHF14AMfNestedHard, 0, "Nested attack for hardened Mifare cards"}, + {"keybrute", CmdHF14AMfKeyBrute, 0, "J_Run's 2nd phase of multiple sector nested authentication key recovery"}, + {"nack", CmdHf14AMfNack, 0, "Test for Mifare NACK bug"}, + {"chk", CmdHF14AMfChk, 0, "Check keys"}, + {"fchk", CmdHF14AMfChk_fast, 0, "Check keys fast, targets all keys on card"}, + {"decrypt", CmdHf14AMfDecryptBytes, 1, "[nt] [ar_enc] [at_enc] [data] - to decrypt snoop or trace"}, + {"-----------", CmdHelp, 1, ""}, + {"dbg", CmdHF14AMfDbg, 0, "Set default debug mode"}, + {"rdbl", CmdHF14AMfRdBl, 0, "Read MIFARE classic block"}, + {"rdsc", CmdHF14AMfRdSc, 0, "Read MIFARE classic sector"}, + {"dump", CmdHF14AMfDump, 0, "Dump MIFARE classic tag to binary file"}, + {"restore", CmdHF14AMfRestore, 0, "Restore MIFARE classic binary file to BLANK tag"}, + {"wrbl", CmdHF14AMfWrBl, 0, "Write MIFARE classic block"}, + {"setmod", CmdHf14AMfSetMod, 0, "Set MIFARE Classic EV1 load modulation strength"}, + {"auth4", CmdHF14AMfAuth4, 0, "ISO14443-4 AES authentication"}, +// {"sniff", CmdHF14AMfSniff, 0, "Sniff card-reader communication"}, + {"-----------", CmdHelp, 1, ""}, + {"sim", CmdHF14AMf1kSim, 0, "Simulate MIFARE card"}, + {"eclr", CmdHF14AMfEClear, 0, "Clear simulator memory block"}, + {"eget", CmdHF14AMfEGet, 0, "Get simulator memory block"}, + {"eset", CmdHF14AMfESet, 0, "Set simulator memory block"}, + {"eload", CmdHF14AMfELoad, 0, "Load from file emul dump"}, + {"esave", CmdHF14AMfESave, 0, "Save to file emul dump"}, + {"ecfill", CmdHF14AMfECFill, 0, "Fill simulator memory with help of keys from simulator"}, + {"ekeyprn", CmdHF14AMfEKeyPrn, 0, "Print keys from simulator memory"}, + {"-----------", CmdHelp, 1, ""}, + {"csetuid", CmdHF14AMfCSetUID, 0, "Set UID for magic Chinese card"}, + {"csetblk", CmdHF14AMfCSetBlk, 0, "Write block - Magic Chinese card"}, + {"cgetblk", CmdHF14AMfCGetBlk, 0, "Read block - Magic Chinese card"}, + {"cgetsc", CmdHF14AMfCGetSc, 0, "Read sector - Magic Chinese card"}, + {"cload", CmdHF14AMfCLoad, 0, "Load dump into magic Chinese card"}, + {"csave", CmdHF14AMfCSave, 0, "Save dump from magic Chinese card into file or emulator"}, + {"-----------", CmdHelp, 1, ""}, + {"mad", CmdHF14AMfMAD, 0, "Checks and prints MAD"}, + {"ndef", CmdHFMFNDEF, 0, "Prints NDEF records from card"}, + + {"ice", CmdHF14AMfice, 0, "collect Mifare Classic nonces to file"}, + {NULL, NULL, 0, NULL} +}; + +int CmdHFMF(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdhfmf.h b/client/cmdhfmf.h index c819d5bef..a8e622cc7 100644 --- a/client/cmdhfmf.h +++ b/client/cmdhfmf.h @@ -1,76 +1,76 @@ -//----------------------------------------------------------------------------- -// Copyright (C) 2011 Merlok -// -// 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 MIFARE commands -//----------------------------------------------------------------------------- - -#ifndef CMDHFMF_H__ -#define CMDHFMF_H__ - -#include -#include -#include -#include -#include -#include -#include "proxmark3.h" -#include "iso14443crc.h" -#include "ui.h" -#include "cmdparser.h" -#include "common.h" -#include "util.h" -#include "mifare.h" // nonces_t struct -#include "mifare/mfkey.h" // mfkey32_moebious -#include "cmdhfmfhard.h" -#include "mifare/mifarehost.h" // icesector_t, sector_t -#include "util_posix.h" // msclock -#include "mifare/mifaredefault.h" // mifare default key array -#include "cmdhf14a.h" // dropfield -#include "cliparser/cliparser.h" // argtable -#include "hardnested/hardnested_bf_core.h" // SetSIMDInstr - -extern int CmdHFMF(const char *Cmd); - -extern int CmdHF14AMfList(const char *Cmd); -extern int CmdHF14AMfDbg(const char* cmd); -extern int CmdHF14AMfRdBl(const char* cmd); -extern int CmdHF14AMfURdBl(const char* cmd); -extern int CmdHF14AMfRdSc(const char* cmd); -extern int CmdHF14SMfURdCard(const char* cmd); -extern int CmdHF14AMfDump(const char* cmd); -extern int CmdHF14AMfRestore(const char* cmd); -extern int CmdHF14AMfWrBl(const char* cmd); -extern int CmdHF14AMfUWrBl(const char* cmd); -extern int CmdHF14AMfChk(const char* cmd); -extern int CmdHF14AMfDarkside(const char* cmd); -extern int CmdHF14AMfNested(const char* cmd); -extern int CmdHF14AMfNestedHard(const char *Cmd); -//extern int CmdHF14AMfSniff(const char* cmd); -extern int CmdHF14AMf1kSim(const char* cmd); -extern int CmdHF14AMfKeyBrute(const char *Cmd); -extern int CmdHF14AMfEClear(const char* cmd); -extern int CmdHF14AMfEGet(const char* cmd); -extern int CmdHF14AMfESet(const char* cmd); -extern int CmdHF14AMfELoad(const char* cmd); -extern int CmdHF14AMfESave(const char* cmd); -extern int CmdHF14AMfECFill(const char* cmd); -extern int CmdHF14AMfEKeyPrn(const char* cmd); -extern int CmdHF14AMfCSetUID(const char* cmd); -extern int CmdHF14AMfCSetBlk(const char* cmd); -extern int CmdHF14AMfCGetBlk(const char* cmd); -extern int CmdHF14AMfCGetSc(const char* cmd); -extern int CmdHF14AMfCLoad(const char* cmd); -extern int CmdHF14AMfCSave(const char* cmd); -extern int CmdHf14MfDecryptBytes(const char *Cmd); -extern int CmdHf14AMfSetMod(const char *Cmd); -extern int CmdHf14AMfNack(const char *Cmd); - -void showSectorTable(void); -void readerAttack(nonces_t data, bool setEmulatorMem, bool verbose); -void printKeyTable( uint8_t sectorscnt, sector_t *e_sector ); -void printKeyTable_fast( uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo ); -#endif +//----------------------------------------------------------------------------- +// Copyright (C) 2011 Merlok +// +// 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 MIFARE commands +//----------------------------------------------------------------------------- + +#ifndef CMDHFMF_H__ +#define CMDHFMF_H__ + +#include +#include +#include +#include +#include +#include +#include "proxmark3.h" +#include "iso14443crc.h" +#include "ui.h" +#include "cmdparser.h" +#include "common.h" +#include "util.h" +#include "mifare.h" // nonces_t struct +#include "mifare/mfkey.h" // mfkey32_moebious +#include "cmdhfmfhard.h" +#include "mifare/mifarehost.h" // icesector_t, sector_t +#include "util_posix.h" // msclock +#include "mifare/mifaredefault.h" // mifare default key array +#include "cmdhf14a.h" // dropfield +#include "cliparser/cliparser.h" // argtable +#include "hardnested/hardnested_bf_core.h" // SetSIMDInstr + +extern int CmdHFMF(const char *Cmd); + +extern int CmdHF14AMfList(const char *Cmd); +extern int CmdHF14AMfDbg(const char* cmd); +extern int CmdHF14AMfRdBl(const char* cmd); +extern int CmdHF14AMfURdBl(const char* cmd); +extern int CmdHF14AMfRdSc(const char* cmd); +extern int CmdHF14SMfURdCard(const char* cmd); +extern int CmdHF14AMfDump(const char* cmd); +extern int CmdHF14AMfRestore(const char* cmd); +extern int CmdHF14AMfWrBl(const char* cmd); +extern int CmdHF14AMfUWrBl(const char* cmd); +extern int CmdHF14AMfChk(const char* cmd); +extern int CmdHF14AMfDarkside(const char* cmd); +extern int CmdHF14AMfNested(const char* cmd); +extern int CmdHF14AMfNestedHard(const char *Cmd); +//extern int CmdHF14AMfSniff(const char* cmd); +extern int CmdHF14AMf1kSim(const char* cmd); +extern int CmdHF14AMfKeyBrute(const char *Cmd); +extern int CmdHF14AMfEClear(const char* cmd); +extern int CmdHF14AMfEGet(const char* cmd); +extern int CmdHF14AMfESet(const char* cmd); +extern int CmdHF14AMfELoad(const char* cmd); +extern int CmdHF14AMfESave(const char* cmd); +extern int CmdHF14AMfECFill(const char* cmd); +extern int CmdHF14AMfEKeyPrn(const char* cmd); +extern int CmdHF14AMfCSetUID(const char* cmd); +extern int CmdHF14AMfCSetBlk(const char* cmd); +extern int CmdHF14AMfCGetBlk(const char* cmd); +extern int CmdHF14AMfCGetSc(const char* cmd); +extern int CmdHF14AMfCLoad(const char* cmd); +extern int CmdHF14AMfCSave(const char* cmd); +extern int CmdHf14MfDecryptBytes(const char *Cmd); +extern int CmdHf14AMfSetMod(const char *Cmd); +extern int CmdHf14AMfNack(const char *Cmd); + +void showSectorTable(void); +void readerAttack(nonces_t data, bool setEmulatorMem, bool verbose); +void printKeyTable( uint8_t sectorscnt, sector_t *e_sector ); +void printKeyTable_fast( uint8_t sectorscnt, icesector_t *e_sector, uint64_t bar, uint64_t foo ); +#endif diff --git a/client/cmdlft55xx.c b/client/cmdlft55xx.c index 9a1e6cc71..e94ec84fe 100644 --- a/client/cmdlft55xx.c +++ b/client/cmdlft55xx.c @@ -1,2056 +1,2056 @@ -//----------------------------------------------------------------------------- -// -// 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. -//----------------------------------------------------------------------------- -// Low frequency T55xx commands -//----------------------------------------------------------------------------- -#include "cmdlft55xx.h" - -// Default configuration -t55xx_conf_block_t config = { .modulation = DEMOD_ASK, .inverted = false, .offset = 0x00, .block0 = 0x00, .Q5 = false }; - -t55xx_conf_block_t Get_t55xx_Config(){ - return config; -} -void Set_t55xx_Config(t55xx_conf_block_t conf){ - config = conf; -} - -int usage_t55xx_config(){ - PrintAndLogEx(NORMAL, "Usage: lf t55xx config [d ] [i 1] [o ] [Q5]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - This help"); - PrintAndLogEx(NORMAL, " b <8|16|32|40|50|64|100|128> - Set bitrate"); - PrintAndLogEx(NORMAL, " d - Set demodulation FSK / ASK / PSK / NRZ / Biphase / Biphase A"); - PrintAndLogEx(NORMAL, " i [1] - Invert data signal, defaults to normal"); - PrintAndLogEx(NORMAL, " o [offset] - Set offset, where data should start decode in bitstream"); - PrintAndLogEx(NORMAL, " Q5 - Set as Q5(T5555) chip instead of T55x7"); - PrintAndLogEx(NORMAL, " ST - Set Sequence Terminator on"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx config d FSK - FSK demodulation"); - PrintAndLogEx(NORMAL, " lf t55xx config d FSK i 1 - FSK demodulation, inverse data"); - PrintAndLogEx(NORMAL, " lf t55xx config d FSK i 1 o 3 - FSK demodulation, inverse data, offset=3,start from position 3 to decode data"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_read(){ - PrintAndLogEx(NORMAL, "Usage: lf t55xx read [b ] [p ] "); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b - block number to read. Between 0-7"); - PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); - PrintAndLogEx(NORMAL, " o - OPTIONAL override safety check"); - PrintAndLogEx(NORMAL, " 1 - OPTIONAL read Page 1 instead of Page 0"); - PrintAndLogEx(NORMAL, " ****WARNING****"); - PrintAndLogEx(NORMAL, " Use of read with password on a tag not configured for a pwd"); - PrintAndLogEx(NORMAL, " can damage the tag"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx read b 0 - read data from block 0"); - PrintAndLogEx(NORMAL, " lf t55xx read b 0 p feedbeef - read data from block 0 password feedbeef"); - PrintAndLogEx(NORMAL, " lf t55xx read b 0 p feedbeef o - read data from block 0 password feedbeef safety check"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_write(){ - PrintAndLogEx(NORMAL, "Usage: lf t55xx write [b ] [d ] [p ] [1] [t]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " b - block number to write. Between 0-7"); - PrintAndLogEx(NORMAL, " d - 4 bytes of data to write (8 hex characters)"); - PrintAndLogEx(NORMAL, " p - OPTIONAL password 4bytes (8 hex characters)"); - PrintAndLogEx(NORMAL, " 1 - OPTIONAL write Page 1 instead of Page 0"); - PrintAndLogEx(NORMAL, " t - OPTIONAL test mode write - ****DANGER****"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx write b 3 d 11223344 - write 11223344 to block 3"); - PrintAndLogEx(NORMAL, " lf t55xx write b 3 d 11223344 p feedbeef - write 11223344 to block 3 password feedbeef"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_trace() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx trace [1]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx trace"); - PrintAndLogEx(NORMAL, " lf t55xx trace 1"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_info() { - PrintAndLogEx(NORMAL, "Usage: lf t55xx info [1]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx info"); - PrintAndLogEx(NORMAL, " lf t55xx info 1"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_dump(){ - PrintAndLogEx(NORMAL, "Usage: lf t55xx dump [o]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " - OPTIONAL password 4bytes (8 hex symbols)"); - PrintAndLogEx(NORMAL, " o - OPTIONAL override, force pwd read despite danger to card"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx dump"); - PrintAndLogEx(NORMAL, " lf t55xx dump feedbeef o"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_detect(){ - PrintAndLogEx(NORMAL, "Usage: lf t55xx detect [1] [p ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); - PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx detect"); - PrintAndLogEx(NORMAL, " lf t55xx detect 1"); - PrintAndLogEx(NORMAL, " lf t55xx detect p 11223344"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_detectP1(){ - PrintAndLogEx(NORMAL, "Command: Detect Page 1 of a t55xx chip"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx p1detect [1] [p ]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); - PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx p1detect"); - PrintAndLogEx(NORMAL, " lf t55xx p1detect 1"); - PrintAndLogEx(NORMAL, " lf t55xx p1detect p 11223344"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_wakup(){ - PrintAndLogEx(NORMAL, "Usage: lf t55xx wakeup [h] p "); - PrintAndLogEx(NORMAL, "This commands send the Answer-On-Request command and leaves the readerfield ON afterwards."); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " p - password 4bytes (8 hex symbols)"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx wakeup p 11223344 - send wakeup password"); - return 0; -} -int usage_t55xx_chk(){ - PrintAndLogEx(NORMAL, "This command uses a dictionary attack"); - PrintAndLogEx(NORMAL, "press 'enter' to cancel the command"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx bruteforce [h] [i <*.dic>]"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " m - use dictionary from flashmemory\n"); - PrintAndLogEx(NORMAL, " i <*.dic> - loads a default keys dictionary file <*.dic>"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx bruteforce m"); - PrintAndLogEx(NORMAL, " lf t55xx bruteforce i default_pwd.dic"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_bruteforce(){ - PrintAndLogEx(NORMAL, "This command uses bruteforce to scan a number range"); - PrintAndLogEx(NORMAL, "press 'enter' to cancel the command"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx bruteforce [h] "); - PrintAndLogEx(NORMAL, " password must be 4 bytes (8 hex symbols)"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " - 4 byte hex value to start pwd search at"); - PrintAndLogEx(NORMAL, " - 4 byte hex value to end pwd search at"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx bruteforce aaaaaaaa bbbbbbbb"); - PrintAndLogEx(NORMAL, ""); - return 0; -} -int usage_t55xx_recoverpw(){ - PrintAndLogEx(NORMAL, "This command uses a few tricks to try to recover mangled password"); - PrintAndLogEx(NORMAL, "press 'enter' to cancel the command"); - PrintAndLogEx(NORMAL, "WARNING: this may brick non-password protected chips!"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx recoverpw [password]"); - PrintAndLogEx(NORMAL, " password must be 4 bytes (8 hex symbols)"); - PrintAndLogEx(NORMAL, " default password is 51243648, used by many cloners"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " [password] - 4 byte hex value of password written by cloner"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx recoverpw"); - PrintAndLogEx(NORMAL, " lf t55xx recoverpw 51243648"); - PrintAndLogEx(NORMAL, ""); - return 0; -}int usage_t55xx_wipe(){ - PrintAndLogEx(NORMAL, "Usage: lf t55xx wipe [h] [Q5]"); - PrintAndLogEx(NORMAL, "This commands wipes a tag, fills blocks 1-7 with zeros and a default configuration block"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - this help"); - PrintAndLogEx(NORMAL, " Q5 - indicates to use the T5555 (Q5) default configuration block"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx wipe - wipes a t55x7 tag, config block 0x000880E0"); - PrintAndLogEx(NORMAL, " lf t55xx wipe Q5 - wipes a t5555 Q5 tag, config block 0x6001F004"); - return 0; -} -int usage_lf_deviceconfig(){ - PrintAndLogEx(NORMAL, "Sets t55x7 timings for direkt commands. The timings are set here in Field Clocks (FC), \nwhich is converted to (US) on device"); - PrintAndLogEx(NORMAL, "Usage: lf t55xx deviceconfig a b c d e p"); - PrintAndLogEx(NORMAL, "Options:"); - PrintAndLogEx(NORMAL, " h - This help"); - PrintAndLogEx(NORMAL, " a <8..255> - Set start gap"); - PrintAndLogEx(NORMAL, " b <8..255> - Set write gap"); - PrintAndLogEx(NORMAL, " c <8..255> - Set write ZERO gap"); - PrintAndLogEx(NORMAL, " d <8..255> - Set write ONE gap"); - PrintAndLogEx(NORMAL, " e <8..255> - Set read gap"); - PrintAndLogEx(NORMAL, " p - persist to flashmemory"); - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "Examples:"); - PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 29 b 17 c 15 d 47 e 15 - default T55XX"); - PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 55 b 14 c 21 d 30 - default EM4305"); - PrintAndLogEx(NORMAL, ""); - return 0; -} - -int CmdHelp(const char *Cmd); - -void printT5xxHeader(uint8_t page){ - PrintAndLogEx(NORMAL, "Reading Page %d:", page); - PrintAndLogEx(NORMAL, "blk | hex data | binary | ascii"); - PrintAndLogEx(NORMAL, "----+----------+----------------------------------+-------"); -} - -int CmdT55xxSetConfig(const char *Cmd) { - - uint8_t offset = 0; - char modulation[6] = {0x00}; - char tmp = 0x00; - uint8_t bitRate = 0; - uint8_t rates[9] = {8,16,32,40,50,64,100,128,0}; - uint8_t cmdp = 0; - bool errors = false; - while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { - tmp = tolower(param_getchar(Cmd, cmdp)); - switch(tmp) { - case 'h': - return usage_t55xx_config(); - case 'b': - errors |= param_getdec(Cmd, cmdp+1, &bitRate); - if ( !errors){ - uint8_t i = 0; - for (; i < 9; i++){ - if (rates[i]==bitRate) { - config.bitrate = i; - break; - } - } - if (i==9) errors = true; - } - cmdp+=2; - break; - case 'd': - param_getstr(Cmd, cmdp+1, modulation, sizeof(modulation)); - cmdp += 2; - - if ( strcmp(modulation, "FSK" ) == 0) { - config.modulation = DEMOD_FSK; - } else if ( strcmp(modulation, "FSK1" ) == 0) { - config.modulation = DEMOD_FSK1; - config.inverted=1; - } else if ( strcmp(modulation, "FSK1a" ) == 0) { - config.modulation = DEMOD_FSK1a; - config.inverted=0; - } else if ( strcmp(modulation, "FSK2" ) == 0) { - config.modulation = DEMOD_FSK2; - config.inverted=0; - } else if ( strcmp(modulation, "FSK2a" ) == 0) { - config.modulation = DEMOD_FSK2a; - config.inverted=1; - } else if ( strcmp(modulation, "ASK" ) == 0) { - config.modulation = DEMOD_ASK; - } else if ( strcmp(modulation, "NRZ" ) == 0) { - config.modulation = DEMOD_NRZ; - } else if ( strcmp(modulation, "PSK1" ) == 0) { - config.modulation = DEMOD_PSK1; - } else if ( strcmp(modulation, "PSK2" ) == 0) { - config.modulation = DEMOD_PSK2; - } else if ( strcmp(modulation, "PSK3" ) == 0) { - config.modulation = DEMOD_PSK3; - } else if ( strcmp(modulation, "BIa" ) == 0) { - config.modulation = DEMOD_BIa; - config.inverted=1; - } else if ( strcmp(modulation, "BI" ) == 0) { - config.modulation = DEMOD_BI; - config.inverted=0; - } else { - PrintAndLogEx(WARNING, "Unknown modulation '%s'", modulation); - errors = true; - } - break; - case 'i': - config.inverted = param_getchar(Cmd,cmdp+1) == '1'; - cmdp+=2; - break; - case 'o': - errors |= param_getdec(Cmd, cmdp+1, &offset); - if ( !errors ) - config.offset = offset; - cmdp+=2; - break; - case 'q': - config.Q5 = true; - cmdp++; - break; - case 's': - config.ST = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - - // No args - if (cmdp == 0) return printConfiguration( config ); - - //Validations - if (errors) return usage_t55xx_config(); - - config.block0 = 0; - return printConfiguration ( config ); -} - -int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32_t password){ - //Password mode - if ( usepwd ) { - // try reading the config block and verify that PWD bit is set before doing this! - if ( !override ) { - - if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0 ) ) return 0; - - if ( !tryDetectModulation() ) { - PrintAndLogEx(NORMAL, "Safety Check: Could not detect if PWD bit is set in config block. Exits."); - return 0; - } else { - PrintAndLogEx(NORMAL, "Safety Check: PWD bit is NOT set in config block. Reading without password..."); - usepwd = false; - page1 = false; - } - } else { - PrintAndLogEx(NORMAL, "Safety Check Overriden - proceeding despite risk"); - } - } - - if (!AquireData(page1, block, usepwd, password) ) return 0; - if (!DecodeT55xxBlock()) return 0; - - char blk[10] = {0}; - sprintf(blk, "%02d", block); - printT55xxBlock(blk); - return 1; -} - -int CmdT55xxReadBlock(const char *Cmd) { - uint8_t block = REGULAR_READ_MODE_BLOCK; - uint32_t password = 0; //default to blank Block 7 - bool usepwd = false; - bool override = false; - bool page1 = false; - bool errors = false; - uint8_t cmdp = 0; - while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch ( tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_read(); - case 'b': - errors |= param_getdec(Cmd, cmdp+1, &block); - cmdp += 2; - break; - case 'o': - override = true; - cmdp++; - break; - case 'p': - password = param_get32ex(Cmd, cmdp+1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case '1': - page1 = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - if (errors) return usage_t55xx_read(); - - if (block > 7 && block != REGULAR_READ_MODE_BLOCK ) { - PrintAndLogEx(NORMAL, "Block must be between 0 and 7"); - return 0; - } - - printT5xxHeader(page1); - return T55xxReadBlock(block, page1, usepwd, override, password); -} - -bool DecodeT55xxBlock(){ - - char buf[30] = {0x00}; - char *cmdStr = buf; - int ans = 0; - bool ST = config.ST; - uint8_t bitRate[8] = {8,16,32,40,50,64,100,128}; - DemodBufferLen = 0x00; - - switch( config.modulation ){ - case DEMOD_FSK: - snprintf(cmdStr, sizeof(buf),"%d %d", bitRate[config.bitrate], config.inverted ); - ans = FSKrawDemod(cmdStr, false); - break; - case DEMOD_FSK1: - case DEMOD_FSK1a: - snprintf(cmdStr, sizeof(buf),"%d %d 8 5", bitRate[config.bitrate], config.inverted ); - ans = FSKrawDemod(cmdStr, false); - break; - case DEMOD_FSK2: - case DEMOD_FSK2a: - snprintf(cmdStr, sizeof(buf),"%d %d 10 8", bitRate[config.bitrate], config.inverted ); - ans = FSKrawDemod(cmdStr, false); - break; - case DEMOD_ASK: - snprintf(cmdStr, sizeof(buf),"%d %d 1", bitRate[config.bitrate], config.inverted ); - ans = ASKDemod_ext(cmdStr, false, false, 1, &ST); - break; - case DEMOD_PSK1: - // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - save_restoreGB(GRAPH_SAVE); - CmdLtrim("160"); - snprintf(cmdStr, sizeof(buf),"%d %d 6", bitRate[config.bitrate], config.inverted ); - ans = PSKDemod(cmdStr, false); - //undo trim samples - save_restoreGB(GRAPH_RESTORE); - break; - case DEMOD_PSK2: //inverted won't affect this - case DEMOD_PSK3: //not fully implemented - // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - save_restoreGB(GRAPH_SAVE); - CmdLtrim("160"); - snprintf(cmdStr, sizeof(buf),"%d 0 6", bitRate[config.bitrate] ); - ans = PSKDemod(cmdStr, false); - psk1TOpsk2(DemodBuffer, DemodBufferLen); - //undo trim samples - save_restoreGB(GRAPH_RESTORE); - break; - case DEMOD_NRZ: - snprintf(cmdStr, sizeof(buf),"%d %d 1", bitRate[config.bitrate], config.inverted ); - ans = NRZrawDemod(cmdStr, false); - break; - case DEMOD_BI: - case DEMOD_BIa: - snprintf(cmdStr, sizeof(buf),"0 %d %d 1", bitRate[config.bitrate], config.inverted ); - ans = ASKbiphaseDemod(cmdStr, false); - break; - default: - return false; - } - return (bool) ans; -} - -bool DecodeT5555TraceBlock() { - DemodBufferLen = 0x00; - - // According to datasheet. Always: RF/64, not inverted, Manchester - return (bool) ASKDemod("64 0 1", false, false, 1); -} - -// sanity check. Don't use proxmark if it is offline and you didn't specify useGraphbuf -static int SanityOfflineCheck( bool useGraphBuffer ){ - if ( !useGraphBuffer && IsOffline() ) { - PrintAndLogEx(NORMAL, "Your proxmark3 device is offline. Specify [1] to use graphbuffer data instead"); - return 0; - } - return 1; -} - -int CmdT55xxDetect(const char *Cmd){ - bool errors = false; - bool useGB = false, usepwd = false; - uint32_t password = 0; - uint8_t cmdp = 0; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch ( tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_detect(); - case 'p': - password = param_get32ex(Cmd, cmdp+1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case '1': - // use Graphbuffer data - useGB = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - if (errors) return usage_t55xx_detect(); - - // sanity check. - if (!SanityOfflineCheck(useGB)) return 1; - - if ( !useGB) { - if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password) ) - return 1; - } - - if ( !tryDetectModulation() ) - PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with \'lf t55xx config\'"); - - return 0; -} - -// detect configuration? -bool tryDetectModulation(){ - - t55xx_conf_block_t tests[15]; - int bitRate = 0, clk = 0, firstClockEdge = 0; - uint8_t hits = 0, fc1 = 0, fc2 = 0, ans = 0; - - ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); - - if (ans && ((fc1==10 && fc2==8) || (fc1==8 && fc2==5))) { - if ( FSKrawDemod("0 0", false) && test(DEMOD_FSK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)){ - tests[hits].modulation = DEMOD_FSK; - if (fc1==8 && fc2 == 5) - tests[hits].modulation = DEMOD_FSK1a; - else if (fc1==10 && fc2 == 8) - tests[hits].modulation = DEMOD_FSK2; - tests[hits].bitrate = bitRate; - tests[hits].inverted = false; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - if ( FSKrawDemod("0 1", false) && test(DEMOD_FSK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { - tests[hits].modulation = DEMOD_FSK; - if (fc1 == 8 && fc2 == 5) - tests[hits].modulation = DEMOD_FSK1; - else if (fc1 == 10 && fc2 == 8) - tests[hits].modulation = DEMOD_FSK2a; - tests[hits].bitrate = bitRate; - tests[hits].inverted = true; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - } else { - clk = GetAskClock("", false); - if (clk > 0) { - tests[hits].ST = true; - // "0 0 1 " == clock auto, invert false, maxError 1. - // false = no verbose - // false = no emSearch - // 1 = Ask/Man - // st = true - if ( ASKDemod_ext("0 0 1", false, false, 1, &tests[hits].ST) && test(DEMOD_ASK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { - tests[hits].modulation = DEMOD_ASK; - tests[hits].bitrate = bitRate; - tests[hits].inverted = false; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - ++hits; - } - tests[hits].ST = true; - // "0 0 1 " == clock auto, invert true, maxError 1. - // false = no verbose - // false = no emSearch - // 1 = Ask/Man - // st = true - if ( ASKDemod_ext("0 1 1", false, false, 1, &tests[hits].ST) && test(DEMOD_ASK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { - tests[hits].modulation = DEMOD_ASK; - tests[hits].bitrate = bitRate; - tests[hits].inverted = true; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - ++hits; - } - if ( ASKbiphaseDemod("0 0 0 2", false) && test(DEMOD_BI, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5) ) { - tests[hits].modulation = DEMOD_BI; - tests[hits].bitrate = bitRate; - tests[hits].inverted = false; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - if ( ASKbiphaseDemod("0 0 1 2", false) && test(DEMOD_BIa, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5) ) { - tests[hits].modulation = DEMOD_BIa; - tests[hits].bitrate = bitRate; - tests[hits].inverted = true; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - } - clk = GetNrzClock("", false); - if (clk > 8) { //clock of rf/8 is likely a false positive, so don't use it. - if ( NRZrawDemod("0 0 1", false) && test(DEMOD_NRZ, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { - tests[hits].modulation = DEMOD_NRZ; - tests[hits].bitrate = bitRate; - tests[hits].inverted = false; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - - if ( NRZrawDemod("0 1 1", false) && test(DEMOD_NRZ, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { - tests[hits].modulation = DEMOD_NRZ; - tests[hits].bitrate = bitRate; - tests[hits].inverted = true; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - } - - clk = GetPskClock("", false); - if (clk > 0) { - // allow undo - save_restoreGB(GRAPH_SAVE); - // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - CmdLtrim("160"); - if ( PSKDemod("0 0 6", false) && test(DEMOD_PSK1, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { - tests[hits].modulation = DEMOD_PSK1; - tests[hits].bitrate = bitRate; - tests[hits].inverted = false; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - if ( PSKDemod("0 1 6", false) && test(DEMOD_PSK1, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { - tests[hits].modulation = DEMOD_PSK1; - tests[hits].bitrate = bitRate; - tests[hits].inverted = true; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - //ICEMAN: are these PSKDemod calls needed? - // PSK2 - needs a call to psk1TOpsk2. - if ( PSKDemod("0 0 6", false)) { - psk1TOpsk2(DemodBuffer, DemodBufferLen); - if (test(DEMOD_PSK2, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)){ - tests[hits].modulation = DEMOD_PSK2; - tests[hits].bitrate = bitRate; - tests[hits].inverted = false; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - } // inverse waves does not affect this demod - // PSK3 - needs a call to psk1TOpsk2. - if ( PSKDemod("0 0 6", false)) { - psk1TOpsk2(DemodBuffer, DemodBufferLen); - if (test(DEMOD_PSK3, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)){ - tests[hits].modulation = DEMOD_PSK3; - tests[hits].bitrate = bitRate; - tests[hits].inverted = false; - tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); - tests[hits].ST = false; - ++hits; - } - } // inverse waves does not affect this demod - //undo trim samples - save_restoreGB(GRAPH_RESTORE); - } - } - if ( hits == 1) { - config.modulation = tests[0].modulation; - config.bitrate = tests[0].bitrate; - config.inverted = tests[0].inverted; - config.offset = tests[0].offset; - config.block0 = tests[0].block0; - config.Q5 = tests[0].Q5; - config.ST = tests[0].ST; - printConfiguration( config ); - return true; - } - - bool retval = false; - if ( hits > 1) { - PrintAndLogEx(SUCCESS, "Found [%d] possible matches for modulation.", hits); - for(int i=0; i= DEMOD_FSK1 && modread <= DEMOD_FSK2a) return true; - break; - case DEMOD_ASK: - if (modread == DEMOD_ASK) return true; - break; - case DEMOD_PSK1: - if (modread == DEMOD_PSK1) return true; - break; - case DEMOD_PSK2: - if (modread == DEMOD_PSK2) return true; - break; - case DEMOD_PSK3: - if (modread == DEMOD_PSK3) return true; - break; - case DEMOD_NRZ: - if (modread == DEMOD_NRZ) return true; - break; - case DEMOD_BI: - if (modread == DEMOD_BI) return true; - break; - case DEMOD_BIa: - if (modread == DEMOD_BIa) return true; - break; - default: - return false; - } - return false; -} - -bool testQ5Modulation(uint8_t mode, uint8_t modread){ - switch( mode ){ - case DEMOD_FSK: - if (modread >= 4 && modread <= 5) return true; - break; - case DEMOD_ASK: - if (modread == 0) return true; - break; - case DEMOD_PSK1: - if (modread == 1) return true; - break; - case DEMOD_PSK2: - if (modread == 2) return true; - break; - case DEMOD_PSK3: - if (modread == 3) return true; - break; - case DEMOD_NRZ: - if (modread == 7) return true; - break; - case DEMOD_BI: - if (modread == 6) return true; - break; - default: - return false; - } - return false; -} - -int convertQ5bitRate(uint8_t bitRateRead) { - uint8_t expected[] = {8, 16, 32, 40, 50, 64, 100, 128}; - for (int i=0; i<8; i++) - if (expected[i] == bitRateRead) - return i; - - return -1; -} - -bool testQ5(uint8_t mode, uint8_t *offset, int *fndBitRate, uint8_t clk){ - - if ( DemodBufferLen < 64 ) return false; - uint8_t si = 0; - for (uint8_t idx = 28; idx < 64; idx++){ - si = idx; - if ( PackBits(si, 28, DemodBuffer) == 0x00 ) continue; - - uint8_t safer = PackBits(si, 4, DemodBuffer); si += 4; //master key - uint8_t resv = PackBits(si, 8, DemodBuffer); si += 8; - // 2nibble must be zeroed. - if (safer != 0x6 && safer != 0x9) continue; - if ( resv > 0x00) continue; - //uint8_t pageSel = PackBits(si, 1, DemodBuffer); si += 1; - //uint8_t fastWrite = PackBits(si, 1, DemodBuffer); si += 1; - si += 1+1; - int bitRate = PackBits(si, 6, DemodBuffer)*2 + 2; si += 6; //bit rate - if (bitRate > 128 || bitRate < 8) continue; - - //uint8_t AOR = PackBits(si, 1, DemodBuffer); si += 1; - //uint8_t PWD = PackBits(si, 1, DemodBuffer); si += 1; - //uint8_t pskcr = PackBits(si, 2, DemodBuffer); si += 2; //could check psk cr - //uint8_t inverse = PackBits(si, 1, DemodBuffer); si += 1; - si += 1+1+2+1; - uint8_t modread = PackBits(si, 3, DemodBuffer); si += 3; - uint8_t maxBlk = PackBits(si, 3, DemodBuffer); si += 3; - //uint8_t ST = PackBits(si, 1, DemodBuffer); si += 1; - if (maxBlk == 0) continue; - //test modulation - if (!testQ5Modulation(mode, modread)) continue; - if (bitRate != clk) continue; - *fndBitRate = convertQ5bitRate(bitRate); - if (*fndBitRate < 0) continue; - *offset = idx; - - return true; - } - return false; -} - -bool testBitRate(uint8_t readRate, uint8_t clk){ - uint8_t expected[] = {8, 16, 32, 40, 50, 64, 100, 128}; - if (expected[readRate] == clk) - return true; - - return false; -} - -bool test(uint8_t mode, uint8_t *offset, int *fndBitRate, uint8_t clk, bool *Q5){ - - if ( DemodBufferLen < 64 ) return false; - uint8_t si = 0; - for (uint8_t idx = 28; idx < 64; idx++){ - si = idx; - if ( PackBits(si, 28, DemodBuffer) == 0x00 ) continue; - - uint8_t safer = PackBits(si, 4, DemodBuffer); si += 4; //master key - uint8_t resv = PackBits(si, 4, DemodBuffer); si += 4; //was 7 & +=7+3 //should be only 4 bits if extended mode - // 2nibble must be zeroed. - // moved test to here, since this gets most faults first. - if ( resv > 0x00) continue; - - int bitRate = PackBits(si, 6, DemodBuffer); si += 6; //bit rate (includes extended mode part of rate) - uint8_t extend = PackBits(si, 1, DemodBuffer); si += 1; //bit 15 extended mode - uint8_t modread = PackBits(si, 5, DemodBuffer); si += 5+2+1; - //uint8_t pskcr = PackBits(si, 2, DemodBuffer); si += 2+1; //could check psk cr - //uint8_t nml01 = PackBits(si, 1, DemodBuffer); si += 1+5; //bit 24, 30, 31 could be tested for 0 if not extended mode - //uint8_t nml02 = PackBits(si, 2, DemodBuffer); si += 2; - - //if extended mode - bool extMode =( (safer == 0x6 || safer == 0x9) && extend) ? true : false; - - if (!extMode){ - if (bitRate > 7) continue; - if (!testBitRate(bitRate, clk)) continue; - } else { //extended mode bitrate = same function to calc bitrate as em4x05 - if (EM4x05_GET_BITRATE(bitRate) != clk) continue; - - } - //test modulation - if (!testModulation(mode, modread)) continue; - *fndBitRate = bitRate; - *offset = idx; - *Q5 = false; - return true; - } - if (testQ5(mode, offset, fndBitRate, clk)) { - *Q5 = true; - return true; - } - return false; -} - -void printT55xxBlock(const char *blockNum){ - - uint8_t i = config.offset; - uint8_t endpos = 32 + i; - uint32_t blockData = 0; - uint8_t bits[64] = {0x00}; - - if ( !DemodBufferLen) return; - - if ( endpos > DemodBufferLen){ - PrintAndLogEx(NORMAL, "The configured offset %d is too big. Possible offset: %d)", i, DemodBufferLen-32); - return; - } - - for (; i < endpos; ++i) - bits[i - config.offset] = DemodBuffer[i]; - - blockData = PackBits(0, 32, bits); - uint8_t bytes[4] = {0}; - num_to_bytes(blockData, 4, bytes); - - PrintAndLogEx(NORMAL, " %s | %08X | %s | %s", blockNum, blockData, sprint_bin(bits,32), sprint_ascii(bytes,4)); -} - -int special(const char *Cmd) { - uint32_t blockData = 0; - uint8_t bits[32] = {0x00}; - - PrintAndLogEx(NORMAL, "OFFSET | DATA | BINARY | ASCII"); - PrintAndLogEx(NORMAL, "-------+-------+-------------------------------------+------"); - int i,j = 0; - for (; j < 64; ++j){ - - for (i = 0; i < 32; ++i) - bits[i]=DemodBuffer[j+i]; - - blockData = PackBits(0, 32, bits); - - PrintAndLogEx(NORMAL, "%02d | 0x%08X | %s",j , blockData, sprint_bin(bits,32)); - } - return 0; -} - -int printConfiguration( t55xx_conf_block_t b){ - PrintAndLogEx(NORMAL, "Chip Type : %s", (b.Q5) ? "T5555(Q5)" : "T55x7"); - PrintAndLogEx(NORMAL, "Modulation : %s", GetSelectedModulationStr(b.modulation) ); - PrintAndLogEx(NORMAL, "Bit Rate : %s", GetBitRateStr(b.bitrate, (b.block0 & T55x7_X_MODE && (b.block0>>28==6 || b.block0>>28==9))) ); - PrintAndLogEx(NORMAL, "Inverted : %s", (b.inverted) ? _GREEN_(Yes) : "No" ); - PrintAndLogEx(NORMAL, "Offset : %d", b.offset); - PrintAndLogEx(NORMAL, "Seq. Term. : %s", (b.ST) ? _GREEN_(Yes) : "No" ); - PrintAndLogEx(NORMAL, "Block0 : 0x%08X", b.block0); - PrintAndLogEx(NORMAL, ""); - return 0; -} - -int CmdT55xxWakeUp(const char *Cmd) { - uint32_t password = 0; - uint8_t cmdp = 0; - bool errors = false; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_wakup(); - case 'p': - password = param_get32ex(Cmd, cmdp+1, 0, 16); - cmdp += 2; - errors = false; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - if (errors) return usage_t55xx_wakup(); - - UsbCommand c = {CMD_T55XX_WAKEUP, {password, 0, 0}}; - clearCommandBuffer(); - SendCommand(&c); - PrintAndLogEx(SUCCESS, "Wake up command sent. Try read now"); - return 0; -} - -int CmdT55xxWriteBlock(const char *Cmd) { - uint8_t block = 0xFF; //default to invalid block - uint32_t data = 0; //default to blank Block - uint32_t password = 0; //default to blank Block 7 - bool usepwd = false; - bool page1 = false; - bool gotdata = false; - bool testMode = false; - bool errors = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_write(); - case 'b': - errors |= param_getdec(Cmd, cmdp+1, &block); - cmdp += 2; - break; - case 'd': - data = param_get32ex(Cmd, cmdp+1, 0, 16); - gotdata = true; - cmdp += 2; - break; - case 'p': - password = param_get32ex(Cmd, cmdp+1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case 't': - testMode = true; - cmdp++; - break; - case '1': - page1 = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - if (errors || !gotdata) return usage_t55xx_write(); - - if (block > 7) { - PrintAndLogEx(WARNING, "Block number must be between 0 and 7"); - return 0; - } - - UsbCommand c = {CMD_T55XX_WRITE_BLOCK, {data, block, 0}}; - UsbCommand resp; - c.d.asBytes[0] = (page1) ? 0x2 : 0; - c.d.asBytes[0] |= (testMode) ? 0x4 : 0; - - char pwdStr[16] = {0}; - snprintf(pwdStr, sizeof(pwdStr), "pwd: 0x%08X", password); - - PrintAndLogEx(INFO, "Writing page %d block: %02d data: 0x%08X %s", page1, block, data, (usepwd) ? pwdStr : "" ); - - //Password mode - if (usepwd) { - c.arg[2] = password; - c.d.asBytes[0] |= 0x1; - } - clearCommandBuffer(); - SendCommand(&c); - if ( !WaitForResponseTimeout(CMD_ACK, &resp, 1500 )){ - PrintAndLogEx(WARNING, "Error occurred, device did not ACK write operation. (May be due to old firmware)"); - return 0; - } - return 1; -} - -int CmdT55xxReadTrace(const char *Cmd) { - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) > 1 || cmdp == 'h') return usage_t55xx_trace(); - - bool pwdmode = false; - uint32_t password = 0; - - if (strlen(Cmd) == 0) { - // sanity check. - if (!SanityOfflineCheck(false)) return 1; - - if ( !AquireData( T55x7_PAGE1, REGULAR_READ_MODE_BLOCK, pwdmode, password ) ) - return 1; - } - - if ( config.Q5 ){ - if (!DecodeT5555TraceBlock()) return 1; - } else { - if (!DecodeT55xxBlock()) return 1; - } - - if ( !DemodBufferLen ) return 1; - - RepaintGraphWindow(); - uint8_t repeat = (config.offset > 5) ? 32 : 0; - - uint8_t si = config.offset + repeat; - uint32_t bl1 = PackBits(si, 32, DemodBuffer); - uint32_t bl2 = PackBits(si+32, 32, DemodBuffer); - - if (config.Q5) { - uint32_t hdr = PackBits(si, 9, DemodBuffer); si += 9; - - if (hdr != 0x1FF) { - PrintAndLogEx(FAILED, "Invalid Q5 Trace data header (expected 0x1FF, found %X)", hdr); - return 1; - } - - t5555_tracedata_t data = {.bl1 = bl1, .bl2 = bl2, .icr = 0, .lotidc = '?', .lotid = 0, .wafer = 0, .dw =0}; - - data.icr = PackBits(si, 2, DemodBuffer); si += 2; - data.lotidc = 'Z' - PackBits(si, 2, DemodBuffer); si += 3; - - data.lotid = PackBits(si, 4, DemodBuffer); si += 5; - data.lotid <<= 4; - data.lotid |= PackBits(si, 4, DemodBuffer); si += 5; - data.lotid <<= 4; - data.lotid |= PackBits(si, 4, DemodBuffer); si += 5; - data.lotid <<= 4; - data.lotid |= PackBits(si, 4, DemodBuffer); si += 5; - data.lotid <<= 1; - data.lotid |= PackBits(si, 1, DemodBuffer); si += 1; - - data.wafer = PackBits(si, 3, DemodBuffer); si += 4; - data.wafer <<= 2; - data.wafer |= PackBits(si, 2, DemodBuffer); si += 2; - - data.dw = PackBits(si, 2, DemodBuffer); si += 3; - data.dw <<= 4; - data.dw |= PackBits(si, 4, DemodBuffer); si += 5; - data.dw <<= 4; - data.dw |= PackBits(si, 4, DemodBuffer); si += 5; - data.dw <<= 4; - data.dw |= PackBits(si, 4, DemodBuffer); si += 5; - - printT5555Trace(data, repeat); - - } else { - - t55x7_tracedata_t data = {.bl1 = bl1, .bl2 = bl2, .acl = 0, .mfc = 0, .cid = 0, .year = 0, .quarter = 0, .icr = 0, .lotid = 0, .wafer = 0, .dw = 0}; - - data.acl = PackBits(si, 8, DemodBuffer); si += 8; - if ( data.acl != 0xE0 ) { - PrintAndLogEx(FAILED, "The modulation is most likely wrong since the ACL is not 0xE0. "); - return 1; - } - - data.mfc = PackBits(si, 8, DemodBuffer); si += 8; - data.cid = PackBits(si, 5, DemodBuffer); si += 5; - data.icr = PackBits(si, 3, DemodBuffer); si += 3; - data.year = PackBits(si, 4, DemodBuffer); si += 4; - data.quarter = PackBits(si, 2, DemodBuffer); si += 2; - data.lotid = PackBits(si, 14, DemodBuffer); si += 14; - data.wafer = PackBits(si, 5, DemodBuffer); si += 5; - data.dw = PackBits(si, 15, DemodBuffer); - - time_t t = time(NULL); - struct tm tm = *localtime(&t); - if ( data.year > tm.tm_year-110) - data.year += 2000; - else - data.year += 2010; - - printT55x7Trace(data, repeat); - } - return 0; -} - -void printT55x7Trace( t55x7_tracedata_t data, uint8_t repeat ){ - PrintAndLogEx(NORMAL, "-- T55x7 Trace Information ----------------------------------"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " ACL Allocation class (ISO/IEC 15963-1) : 0x%02X (%d)", data.acl, data.acl); - PrintAndLogEx(NORMAL, " MFC Manufacturer ID (ISO/IEC 7816-6) : 0x%02X (%d) - %s", data.mfc, data.mfc, getTagInfo(data.mfc)); - PrintAndLogEx(NORMAL, " CID : 0x%02X (%d) - %s", data.cid, data.cid, GetModelStrFromCID(data.cid)); - PrintAndLogEx(NORMAL, " ICR IC Revision : %d", data.icr ); - PrintAndLogEx(NORMAL, " Manufactured"); - PrintAndLogEx(NORMAL, " Year/Quarter : %d/%d", data.year, data.quarter); - PrintAndLogEx(NORMAL, " Lot ID : %d", data.lotid ); - PrintAndLogEx(NORMAL, " Wafer number : %d", data.wafer); - PrintAndLogEx(NORMAL, " Die Number : %d", data.dw); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Raw Data - Page 1"); - PrintAndLogEx(NORMAL, " Block 1 : 0x%08X %s", data.bl1, sprint_bin(DemodBuffer+config.offset+repeat,32) ); - PrintAndLogEx(NORMAL, " Block 2 : 0x%08X %s", data.bl2, sprint_bin(DemodBuffer+config.offset+repeat+32,32) ); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - - /* - TRACE - BLOCK O - Bits Definition HEX - 1-8 ACL Allocation class (ISO/IEC 15963-1) 0xE0 - 9-16 MFC Manufacturer ID (ISO/IEC 7816-6) 0x15 Atmel Corporation - 17-21 CID 0x1 = Atmel ATA5577M1 0x2 = Atmel ATA5577M2 - 22-24 ICR IC revision - 25-28 YEAR (BCD encoded) 9 (= 2009) - 29-30 QUARTER 1,2,3,4 - 31-32 LOT ID - - TRACE - BLOCK 1 - 1-12 LOT ID - 13-17 Wafer number - 18-32 DW, die number sequential - */ -} - -void printT5555Trace( t5555_tracedata_t data, uint8_t repeat ){ - PrintAndLogEx(NORMAL, "-- T5555 (Q5) Trace Information -----------------------------"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " ICR IC Revision : %d", data.icr ); - PrintAndLogEx(NORMAL, " Lot : %c%d", data.lotidc, data.lotid); - PrintAndLogEx(NORMAL, " Wafer number : %d", data.wafer); - PrintAndLogEx(NORMAL, " Die Number : %d", data.dw); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Raw Data - Page 1"); - PrintAndLogEx(NORMAL, " Block 1 : 0x%08X %s", data.bl1, sprint_bin(DemodBuffer+config.offset+repeat,32) ); - PrintAndLogEx(NORMAL, " Block 2 : 0x%08X %s", data.bl2, sprint_bin(DemodBuffer+config.offset+repeat+32,32) ); - - /* - ** Q5 ** - TRACE - BLOCK O and BLOCK1 - Bits Definition HEX - 1-9 Header 0x1FF - 10-11 IC Revision - 12-13 Lot ID char - 15-35 Lot ID (NB parity) - 36-41 Wafer number (NB parity) - 42-58 DW, die number sequential (NB parity) - 60-63 Parity bits - 64 Always zero - */ -} - -//need to add Q5 info... -int CmdT55xxInfo(const char *Cmd){ - /* - Page 0 Block 0 Configuration data. - Normal mode - Extended mode - */ - bool pwdmode = false; - uint32_t password = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - - if (strlen(Cmd) > 1 || cmdp == 'h') return usage_t55xx_info(); - - if (strlen(Cmd) == 0) { - // sanity check. - if (!SanityOfflineCheck(false)) return 1; - - if ( !AquireData( T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password ) ) - return 1; - } - - if (!DecodeT55xxBlock()) return 1; - - // too little space to start with - if ( DemodBufferLen < 32) return 1; - - // - //PrintAndLogEx(NORMAL, "Offset+32 ==%d\n DemodLen == %d", config.offset + 32, DemodBufferLen); - - uint8_t si = config.offset; - uint32_t block0 = PackBits(si, 32, DemodBuffer); - uint32_t safer = PackBits(si, 4, DemodBuffer); si += 4; - uint32_t resv = PackBits(si, 7, DemodBuffer); si += 7; - uint32_t dbr = PackBits(si, 3, DemodBuffer); si += 3; - uint32_t extend = PackBits(si, 1, DemodBuffer); si += 1; - uint32_t datamod = PackBits(si, 5, DemodBuffer); si += 5; - uint32_t pskcf = PackBits(si, 2, DemodBuffer); si += 2; - uint32_t aor = PackBits(si, 1, DemodBuffer); si += 1; - uint32_t otp = PackBits(si, 1, DemodBuffer); si += 1; - uint32_t maxblk = PackBits(si, 3, DemodBuffer); si += 3; - uint32_t pwd = PackBits(si, 1, DemodBuffer); si += 1; - uint32_t sst = PackBits(si, 1, DemodBuffer); si += 1; - uint32_t fw = PackBits(si, 1, DemodBuffer); si += 1; - uint32_t inv = PackBits(si, 1, DemodBuffer); si += 1; - uint32_t por = PackBits(si, 1, DemodBuffer); si += 1; - - if (config.Q5) - PrintAndLogEx(NORMAL, _RED_(*** Warning ***) " Config Info read off a Q5 will not display as expected"); - - PrintAndLogEx(NORMAL, ""); - PrintAndLogEx(NORMAL, "-- T55x7 Configuration & Tag Information --------------------"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Safer key : %s", GetSaferStr(safer)); - PrintAndLogEx(NORMAL, " reserved : %d", resv); - PrintAndLogEx(NORMAL, " Data bit rate : %s", GetBitRateStr(dbr, extend)); - PrintAndLogEx(NORMAL, " eXtended mode : %s", (extend) ? _YELLOW_(Yes - Warning) : "No"); - PrintAndLogEx(NORMAL, " Modulation : %s", GetModulationStr(datamod)); - PrintAndLogEx(NORMAL, " PSK clock frequency : %d", pskcf); - PrintAndLogEx(NORMAL, " AOR - Answer on Request : %s", (aor) ? _GREEN_(Yes) : "No"); - PrintAndLogEx(NORMAL, " OTP - One Time Pad : %s", (otp) ? _YELLOW_(Yes - Warning) : "No" ); - PrintAndLogEx(NORMAL, " Max block : %d", maxblk); - PrintAndLogEx(NORMAL, " Password mode : %s", (pwd) ? _GREEN_(Yes) : "No"); - PrintAndLogEx(NORMAL, " Sequence Start Terminator : %s", (sst) ? _GREEN_(Yes) : "No"); - PrintAndLogEx(NORMAL, " Fast Write : %s", (fw) ? _GREEN_(Yes) : "No"); - PrintAndLogEx(NORMAL, " Inverse data : %s", (inv) ? _GREEN_(Yes) : "No"); - PrintAndLogEx(NORMAL, " POR-Delay : %s", (por) ? _GREEN_(Yes) : "No"); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - PrintAndLogEx(NORMAL, " Raw Data - Page 0"); - PrintAndLogEx(NORMAL, " Block 0 : 0x%08X %s", block0, sprint_bin(DemodBuffer + config.offset, 32) ); - PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); - return 0; -} - -int CmdT55xxDump(const char *Cmd){ - - uint32_t password = 0; - bool override = false; - char cmdp = tolower(param_getchar(Cmd, 0)); - if ( cmdp == 'h') return usage_t55xx_dump(); - - bool usepwd = ( strlen(Cmd) > 0); - if ( usepwd ){ - password = param_get32ex(Cmd, 0, 0, 16); - if (param_getchar(Cmd, 1) =='o' ) - override = true; - } - - printT5xxHeader(0); - for ( uint8_t i = 0; i < 8; ++i) - T55xxReadBlock(i, 0, usepwd, override, password); - - printT5xxHeader(1); - for ( uint8_t i = 0; i < 4; i++) - T55xxReadBlock(i, 1, usepwd, override, password); - - return 1; -} - -bool AquireData( uint8_t page, uint8_t block, bool pwdmode, uint32_t password ) { - // arg0 bitmodes: - // bit0 = pwdmode - // bit1 = page to read from - // arg1: which block to read - // arg2: password - uint8_t arg0 = (page<<1) | pwdmode; - UsbCommand c = {CMD_T55XX_READ_BLOCK, {arg0, block, password}}; - clearCommandBuffer(); - SendCommand(&c); - if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2500) ) { - PrintAndLogEx(WARNING, "command execution time out"); - return false; - } - - uint8_t got[8000]; - if ( !GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 4000, true)) { - PrintAndLogEx(WARNING, "command execution time out"); - return false; - } - setGraphBuf(got, sizeof(got)); - // set signal properties low/high/mean/amplitude and is_noise detection - computeSignalProperties(got, sizeof(got)); - RepaintGraphWindow(); - return !getSignalProperties()->isnoise; -} - -char * GetBitRateStr(uint32_t id, bool xmode) { - static char buf[25]; - - char *retStr = buf; - if (xmode) { //xmode bitrate calc is same as em4x05 calc - snprintf(retStr,sizeof(buf),"%d - RF/%d", id, EM4x05_GET_BITRATE(id)); - } else { - switch (id){ - case 0: snprintf(retStr,sizeof(buf),"%d - RF/8",id); break; - case 1: snprintf(retStr,sizeof(buf),"%d - RF/16",id); break; - case 2: snprintf(retStr,sizeof(buf),"%d - RF/32",id); break; - case 3: snprintf(retStr,sizeof(buf),"%d - RF/40",id); break; - case 4: snprintf(retStr,sizeof(buf),"%d - RF/50",id); break; - case 5: snprintf(retStr,sizeof(buf),"%d - RF/64",id); break; - case 6: snprintf(retStr,sizeof(buf),"%d - RF/100",id); break; - case 7: snprintf(retStr,sizeof(buf),"%d - RF/128",id); break; - default: snprintf(retStr,sizeof(buf),"%d - (Unknown)",id); break; - } - } - return buf; -} - -char * GetSaferStr(uint32_t id){ - static char buf[40]; - char *retStr = buf; - - snprintf(retStr,sizeof(buf),"%d",id); - if (id == 6) { - snprintf(retStr,sizeof(buf),"%d - passwd",id); - } - if (id == 9 ){ - snprintf(retStr,sizeof(buf),"%d - testmode",id); - } - - return buf; -} - -char * GetModulationStr( uint32_t id){ - static char buf[60]; - char *retStr = buf; - - switch (id){ - case 0: snprintf(retStr,sizeof(buf),"%d - DIRECT (ASK/NRZ)",id); break; - case 1: snprintf(retStr,sizeof(buf),"%d - PSK 1 phase change when input changes",id); break; - case 2: snprintf(retStr,sizeof(buf),"%d - PSK 2 phase change on bitclk if input high",id); break; - case 3: snprintf(retStr,sizeof(buf),"%d - PSK 3 phase change on rising edge of input",id); break; - case 4: snprintf(retStr,sizeof(buf),"%d - FSK 1 RF/8 RF/5",id); break; - case 5: snprintf(retStr,sizeof(buf),"%d - FSK 2 RF/8 RF/10",id); break; - case 6: snprintf(retStr,sizeof(buf),"%d - FSK 1a RF/5 RF/8",id); break; - case 7: snprintf(retStr,sizeof(buf),"%d - FSK 2a RF/10 RF/8",id); break; - case 8: snprintf(retStr,sizeof(buf),"%d - Manchester",id); break; - case 16: snprintf(retStr,sizeof(buf),"%d - Biphase",id); break; - case 0x18:snprintf(retStr,sizeof(buf),"%d - Biphase a - AKA Conditional Dephase Encoding(CDP)",id); break; - case 17: snprintf(retStr,sizeof(buf),"%d - Reserved",id); break; - default: snprintf(retStr,sizeof(buf),"0x%02X (Unknown)",id); break; - } - return buf; -} - -char * GetModelStrFromCID(uint32_t cid){ - - static char buf[10]; - char *retStr = buf; - - if (cid == 1) snprintf(retStr, sizeof(buf),"ATA5577M1"); - if (cid == 2) snprintf(retStr, sizeof(buf),"ATA5577M2"); - return buf; -} - -char * GetSelectedModulationStr( uint8_t id){ - - static char buf[20]; - char *retStr = buf; - - switch (id){ - case DEMOD_FSK: snprintf(retStr,sizeof(buf),"FSK"); break; - case DEMOD_FSK1: snprintf(retStr,sizeof(buf),"FSK1"); break; - case DEMOD_FSK1a: snprintf(retStr,sizeof(buf),"FSK1a"); break; - case DEMOD_FSK2: snprintf(retStr,sizeof(buf),"FSK2"); break; - case DEMOD_FSK2a: snprintf(retStr,sizeof(buf),"FSK2a"); break; - case DEMOD_ASK: snprintf(retStr,sizeof(buf),"ASK"); break; - case DEMOD_NRZ: snprintf(retStr,sizeof(buf),"DIRECT/NRZ"); break; - case DEMOD_PSK1: snprintf(retStr,sizeof(buf),"PSK1"); break; - case DEMOD_PSK2: snprintf(retStr,sizeof(buf),"PSK2"); break; - case DEMOD_PSK3: snprintf(retStr,sizeof(buf),"PSK3"); break; - case DEMOD_BI: snprintf(retStr,sizeof(buf),"BIPHASE"); break; - case DEMOD_BIa: snprintf(retStr,sizeof(buf),"BIPHASEa - (CDP)"); break; - default: snprintf(retStr,sizeof(buf),"(Unknown)"); break; - } - return buf; -} - -void t55x7_create_config_block( int tagtype ){ - - /* - T55X7_DEFAULT_CONFIG_BLOCK, T55X7_RAW_CONFIG_BLOCK - T55X7_EM_UNIQUE_CONFIG_BLOCK, T55X7_FDXB_CONFIG_BLOCK, - T55X7_FDXB_CONFIG_BLOCK, T55X7_HID_26_CONFIG_BLOCK, T55X7_INDALA_64_CONFIG_BLOCK, T55X7_INDALA_224_CONFIG_BLOCK - T55X7_GUARDPROXII_CONFIG_BLOCK, T55X7_VIKING_CONFIG_BLOCK, T55X7_NORALYS_CONFIG_BLOCK, T55X7_IOPROX_CONFIG_BLOCK - */ - static char buf[60]; - char *retStr = buf; - - switch (tagtype){ - case 0: snprintf(retStr, sizeof(buf),"%08X - T55X7 Default", T55X7_DEFAULT_CONFIG_BLOCK); break; - case 1: snprintf(retStr, sizeof(buf),"%08X - T55X7 Raw", T55X7_RAW_CONFIG_BLOCK); break; - case 2: snprintf(retStr, sizeof(buf),"%08X - T5555 Q5 Default", T5555_DEFAULT_CONFIG_BLOCK); break; - default: - break; - } - PrintAndLogEx(NORMAL, buf); -} - -int CmdResetRead(const char *Cmd) { - UsbCommand c = {CMD_T55XX_RESET_READ, {0,0,0}}; - clearCommandBuffer(); - SendCommand(&c); - if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2500) ) { - PrintAndLogEx(WARNING, "command execution time out"); - return 0; - } - - uint8_t got[BIGBUF_SIZE-1]; - if ( !GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 2500, false)) { - PrintAndLogEx(WARNING, "command execution time out"); - return 0; - } - setGraphBuf(got, sizeof(got)); - return 1; -} - -int CmdT55xxWipe(const char *Cmd) { - char writeData[20] = {0}; - char *ptrData = writeData; - char cmdp = param_getchar(Cmd, 0); - if ( cmdp == 'h' || cmdp == 'H') return usage_t55xx_wipe(); - - bool Q5 = (cmdp == 'q' || cmdp == 'Q'); - - // Try with the default password to reset block 0 - // With a pwd should work even if pwd bit not set - PrintAndLogEx(INFO, "\nBeginning Wipe of a T55xx tag (assuming the tag is not password protected)\n"); - - if ( Q5 ) - snprintf(ptrData,sizeof(writeData),"b 0 d 6001F004 p 0"); - else - snprintf(ptrData,sizeof(writeData),"b 0 d 000880E0 p 0"); - - if (!CmdT55xxWriteBlock(ptrData)) PrintAndLogEx(WARNING, "Error writing blk 0"); - - for (uint8_t blk = 1; blk<8; blk++) { - - snprintf(ptrData,sizeof(writeData),"b %d d 0", blk); - - if (!CmdT55xxWriteBlock(ptrData)) PrintAndLogEx(WARNING, "Error writing blk %d", blk); - - memset(writeData, 0x00, sizeof(writeData)); - } - return 0; -} - -bool IsCancelled(void) { - if (ukbhit()) { - int gc = getchar(); (void)gc; - PrintAndLogEx(NORMAL, "\naborted via keyboard!\n"); - return true; - } - return false; -} - -int CmdT55xxChkPwds(const char *Cmd) { - // load a default pwd file. - char line[9]; - char filename[FILE_PATH_SIZE] = {0}; - int keycnt = 0; - uint8_t stKeyBlock = 20; - uint8_t *keyBlock = NULL, *p = NULL; - bool found = false; - uint8_t timeout = 0; - - memset(line, 0, sizeof(line)); - - char cmdp = tolower(param_getchar(Cmd, 0)); - if (strlen(Cmd) == 0 || cmdp == 'h') return usage_t55xx_chk(); - - /* - if ( T55xxReadBlock(7, 0, 0, 0, 0) ) { - - // now try to validate it.. - PrintAndLogEx(WARNING, "\n Block 7 was readable"); - return 1; - } - */ - - uint64_t t1 = msclock(); - - if ( cmdp == 'm' ) { - UsbCommand c = {CMD_T55XX_CHKPWDS, {0,0,0} }; - clearCommandBuffer(); - SendCommand(&c); - UsbCommand resp; - - while ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { - timeout++; - printf("."); fflush(stdout); - if (timeout > 180) { - PrintAndLogEx(WARNING, "\nno response from Proxmark. Aborting..."); - return 2; - } - } - - if ( resp.arg[0] ) { - PrintAndLogEx(SUCCESS, "\nFound a candidate [ %08X ]. Trying to validate", resp.arg[1]); - - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, resp.arg[1])) { - PrintAndLogEx(INFO, "Aquireing data from device failed. Quitting"); - return 2; - } - - found = tryDetectModulation(); - if (found) { - PrintAndLogEx(SUCCESS, "Found valid password: [ %08X ]", resp.arg[1]); - } else { - PrintAndLogEx(WARNING, "Password NOT found."); - } - } else { - PrintAndLogEx(WARNING, "Password NOT found."); - } - - goto out; - } - - keyBlock = calloc(stKeyBlock, 4); - if (keyBlock == NULL) return 1; - - if (cmdp == 'i') { - - int len = strlen(Cmd+2); - if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; - memcpy(filename, Cmd+2, len); - - FILE * f = fopen( filename , "r"); - if ( !f ) { - PrintAndLogEx(FAILED, "File: " _YELLOW_(%s) ": not found or locked.", filename); - free(keyBlock); - return 1; - } - - while( fgets(line, sizeof(line), f) ){ - if (strlen(line) < 8 || line[7] == '\n') continue; - - //goto next line - while (fgetc(f) != '\n' && !feof(f)) ; - - //The line start with # is comment, skip - if( line[0]=='#' ) continue; - - if (!isxdigit(line[0])) { - PrintAndLogEx(WARNING, "File content error. '%s' must include 8 HEX symbols", line); - continue; - } - - line[8] = 0; - - // realloc keyblock array size. - if ( stKeyBlock - keycnt < 2) { - p = realloc(keyBlock, 4 * (stKeyBlock += 10)); - if (!p) { - PrintAndLogEx(WARNING, "Cannot allocate memory for defaultKeys"); - free(keyBlock); - if (f) - fclose(f); - return 2; - } - keyBlock = p; - } - // clear mem - memset(keyBlock + 4 * keycnt, 0, 4); - - num_to_bytes( strtoll(line, NULL, 16), 4, keyBlock + 4*keycnt); - -// PrintAndLogEx(NORMAL, "chk custom pwd[%2d] %08X", keycnt, bytes_to_num(keyBlock + 4 * keycnt, 4) ); - keycnt++; - memset(line, 0, sizeof(line)); - } - - if (f) - fclose(f); - - if (keycnt == 0) { - PrintAndLogEx(WARNING, "No keys found in file"); - free(keyBlock); - return 1; - } - PrintAndLogEx(SUCCESS, "Loaded %d keys", keycnt); - - // loop - uint64_t testpwd = 0x00; - for (uint16_t c = 0; c < keycnt; ++c ) { - - if ( IsOffline() ) { - PrintAndLogEx(WARNING, "Device offline\n"); - free(keyBlock); - return 2; - } - - if (IsCancelled()) { - free(keyBlock); - return 0; - } - - testpwd = bytes_to_num(keyBlock + 4*c, 4); - - PrintAndLogEx(INFO, "Testing %08X", testpwd); - - if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, testpwd)) { - PrintAndLogEx(INFO, "Aquireing data from device failed. Quitting"); - free(keyBlock); - return 0; - } - - found = tryDetectModulation(); - if ( found ) - break; - - } - if ( found ) - PrintAndLogEx(SUCCESS, "Found valid password: [ %08X ]", testpwd); - else - PrintAndLogEx(WARNING, "Password NOT found."); - } - - free(keyBlock); - -out: - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "\nTime in bruteforce: %.0f seconds\n", (float)t1/1000.0); - return 0; -} - -int CmdT55xxBruteForce(const char *Cmd) { - - uint32_t start_password = 0x00000000; //start password - uint32_t end_password = 0xFFFFFFFF; //end password - uint32_t curr = 0; - bool found = false; - - - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h') return usage_t55xx_bruteforce(); - - uint64_t t1 = msclock(); - - // Try to read Block 7, first :) - - // incremental pwd range search - start_password = param_get32ex(Cmd, 0, 0, 16); - end_password = param_get32ex(Cmd, 1, 0, 16); - - curr = start_password; - - if ( start_password >= end_password ) { - return usage_t55xx_bruteforce(); - } - - PrintAndLogEx(INFO, "Search password range [%08X -> %08X]", start_password, end_password); - - while ( !found ){ - - printf("."); fflush(stdout); - - if (IsCancelled()) { - return 0; - } - - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr)) { - PrintAndLogEx(WARNING, "Aquiring data from device failed. Quitting"); - return 0; - } - - found = tryDetectModulation(); - - if (curr == end_password) - break; - curr++; - } - - PrintAndLogEx(NORMAL, ""); - - if (found) - PrintAndLogEx(SUCCESS, "Found valid password: [ %08X ]", curr); - else - PrintAndLogEx(WARNING, "Password NOT found. Last tried: [ %08X ]", --curr); - - t1 = msclock() - t1; - PrintAndLogEx(SUCCESS, "\nTime in bruteforce: %.0f seconds\n", (float)t1/1000.0); - return 0; -} - -int tryOnePassword(uint32_t password) { - PrintAndLogEx(INFO, "Trying password %08x", password); - if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, password)) { - PrintAndLogEx(NORMAL, "Acquire data from device failed. Quitting"); - return -1; - } - - if (tryDetectModulation()) - return 1; - else - return 0; -} - -int CmdT55xxRecoverPW(const char *Cmd) { - int bit = 0; - uint32_t orig_password = 0x0; - uint32_t curr_password = 0x0; - uint32_t prev_password = 0xffffffff; - uint32_t mask = 0x0; - int found = 0; - char cmdp = tolower(param_getchar(Cmd, 0)); - if (cmdp == 'h' ) return usage_t55xx_recoverpw(); - - orig_password = param_get32ex(Cmd, 0, 0x51243648, 16); //password used by handheld cloners - - // first try fliping each bit in the expected password - while (bit < 32) { - curr_password = orig_password ^ ( 1 << bit ); - found = tryOnePassword(curr_password); - if (found == -1) return 0; - bit++; - - if (IsCancelled()) return 0; - } - - // now try to use partial original password, since block 7 should have been completely - // erased during the write sequence and it is possible that only partial password has been - // written - // not sure from which end the bit bits are written, so try from both ends - // from low bit to high bit - bit = 0; - while (bit < 32) { - mask += ( 1 << bit ); - curr_password = orig_password & mask; - // if updated mask didn't change the password, don't try it again - if (prev_password == curr_password) { - bit++; - continue; - } - found = tryOnePassword(curr_password); - if (found == -1) return 0; - bit++; - prev_password = curr_password; - - if (IsCancelled()) return 0; - } - - // from high bit to low - bit = 0; - mask = 0xffffffff; - while (bit < 32) { - mask -= ( 1 << bit ); - curr_password = orig_password & mask; - // if updated mask didn't change the password, don't try it again - if (prev_password == curr_password) { - bit++; - continue; - } - found = tryOnePassword(curr_password); - if (found == -1) - return 0; - bit++; - prev_password = curr_password; - - if (IsCancelled()) return 0; - } - - PrintAndLogEx(NORMAL, ""); - - if (found == 1) - PrintAndLogEx(SUCCESS, "Found valid password: [%08x]", curr_password); - else - PrintAndLogEx(WARNING, "Password NOT found."); - - return 0; -} - -// note length of data returned is different for different chips. -// some return all page 1 (64 bits) and others return just that block (32 bits) -// unfortunately the 64 bits makes this more likely to get a false positive... -bool tryDetectP1(bool getData) { - uint8_t preamble[] = {1,1,1,0,0,0,0,0,0,0,0,1,0,1,0,1}; - size_t startIdx = 0; - uint8_t fc1 = 0, fc2 = 0, ans = 0; - int clk = 0, firstClockEdge = 0; - bool st = true; - - if ( getData ) { - if ( !AquireData(T55x7_PAGE1, 1, false, 0) ) - return false; - } - - // try fsk clock detect. if successful it cannot be any other type of modulation... (in theory...) - ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); - if (ans && ((fc1==10 && fc2==8) || (fc1==8 && fc2==5))) { - if ( FSKrawDemod("0 0", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - if ( FSKrawDemod("0 1", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - return false; - } - - // try ask clock detect. it could be another type even if successful. - clk = GetAskClock("", false); - if (clk > 0) { - if ( ASKDemod_ext("0 0 1", false, false, 1, &st) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - st = true; - if ( ASKDemod_ext("0 1 1", false, false, 1, &st) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - if ( ASKbiphaseDemod("0 0 0 2", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - if ( ASKbiphaseDemod("0 0 1 2", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - } - - // try NRZ clock detect. it could be another type even if successful. - clk = GetNrzClock("", false); //has the most false positives :( - if (clk > 0) { - if ( NRZrawDemod("0 0 1", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - if ( NRZrawDemod("0 1 1", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - return true; - } - } - - // Fewer card uses PSK - // try psk clock detect. if successful it cannot be any other type of modulation... (in theory...) - clk = GetPskClock("", false); - if (clk > 0) { - // allow undo - // save_restoreGB(1); - // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) - //CmdLtrim("160"); - if ( PSKDemod("0 0 6", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - //save_restoreGB(0); - return true; - } - if ( PSKDemod("0 1 6", false) && - preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - //save_restoreGB(0); - return true; - } - // PSK2 - needs a call to psk1TOpsk2. - if ( PSKDemod("0 0 6", false)) { - psk1TOpsk2(DemodBuffer, DemodBufferLen); - if (preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && - (DemodBufferLen == 32 || DemodBufferLen == 64) ) { - //save_restoreGB(0); - return true; - } - } // inverse waves does not affect PSK2 demod - //undo trim samples - //save_restoreGB(0); - // no other modulation clocks = 2 or 4 so quit searching - if (fc1 != 8) return false; - } - - return false; -} -// does this need to be a callable command? -int CmdT55xxDetectPage1(const char *Cmd){ - bool errors = false; - bool useGB = false; - bool usepwd = false; - uint32_t password = 0; - uint8_t cmdp = 0; - - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_t55xx_detectP1(); - case 'p': - password = param_get32ex(Cmd, cmdp+1, 0, 16); - usepwd = true; - cmdp += 2; - break; - case '1': - // use Graphbuffer data - useGB = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = true; - break; - } - } - if (errors) return usage_t55xx_detectP1(); - - if ( !useGB ) { - if ( !AquireData(T55x7_PAGE1, 1, usepwd, password) ) - return false; - } - bool success = tryDetectP1(false); - if (success) PrintAndLogEx(SUCCESS, "T55xx chip found!"); - return success; -} - -int CmdT55xxSetDeviceConfig(const char *Cmd){ - uint8_t startgap = 0, writegap = 0; - uint8_t write0 = 0, write1 = 0, readgap = 0; - bool errors = false, shall_persist = false; - uint8_t cmdp = 0; - while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { - switch (tolower(param_getchar(Cmd, cmdp))) { - case 'h': - return usage_lf_deviceconfig(); - case 'a': - errors |= param_getdec(Cmd, cmdp+1, &startgap); - cmdp += 2; - break; - case 'b': - errors |= param_getdec(Cmd, cmdp+1, &writegap); - cmdp += 2; - break; - case 'c': - errors |= param_getdec(Cmd, cmdp+1, &write0); - cmdp += 2; - break; - case 'd': - errors |= param_getdec(Cmd, cmdp+1, &write1); - cmdp += 2; - break; - case 'e': - errors |= param_getdec(Cmd, cmdp+1, &readgap); - cmdp += 2; - break; - case 'p': - shall_persist = true; - cmdp++; - break; - default: - PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); - errors = 1; - break; - } - } - - //Validations - if (errors || cmdp == 0) return usage_lf_deviceconfig(); - - t55xx_config config = { startgap*8, writegap*8, write0*8, write1*8, readgap*8 }; - - UsbCommand c = {CMD_SET_LF_T55XX_CONFIG, {shall_persist,0,0} }; - memcpy(c.d.asBytes, &config, sizeof(t55xx_config)); - clearCommandBuffer(); - SendCommand(&c); - return 0; -} - -static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"bruteforce", CmdT55xxBruteForce,0, " [i <*.dic>] Simple bruteforce attack to find password"}, - {"config", CmdT55xxSetConfig, 1, "Set/Get T55XX configuration (modulation, inverted, offset, rate)"}, - {"chk", CmdT55xxChkPwds, 1, "Check passwords"}, - {"detect", CmdT55xxDetect, 1, "[1] Try detecting the tag modulation from reading the configuration block."}, - {"deviceconfig", CmdT55xxSetDeviceConfig, 1, "Set/Get T55XX device configuration (startgap, writegap, write0, write1, readgap"}, - {"p1detect", CmdT55xxDetectPage1,1, "[1] Try detecting if this is a t55xx tag by reading page 1"}, - {"dump", CmdT55xxDump, 0, "[password] [o] Dump T55xx card block 0-7. Optional [password], [override]"}, - {"info", CmdT55xxInfo, 1, "[1] Show T55x7 configuration data (page 0/ blk 0)"}, - {"read", CmdT55xxReadBlock, 0, "b p [password] [o] [1] -- Read T55xx block data. Optional [p password], [override], [page1]"}, - {"resetread", CmdResetRead, 0, "Send Reset Cmd then lf read the stream to attempt to identify the start of it (needs a demod and/or plot after)"}, - {"recoverpw", CmdT55xxRecoverPW, 0, "[password] Try to recover from bad password write from a cloner. Only use on PW protected chips!"}, - {"special", special, 0, "Show block changes with 64 different offsets"}, - {"trace", CmdT55xxReadTrace, 1, "[1] Show T55x7 traceability data (page 1/ blk 0-1)"}, - {"wakeup", CmdT55xxWakeUp, 0, "Send AOR wakeup command"}, - {"wipe", CmdT55xxWipe, 0, "[q] Wipe a T55xx tag and set defaults (will destroy any data on tag)"}, - {"write", CmdT55xxWriteBlock,0, "b d p [password] [1] -- Write T55xx block data. Optional [p password], [page1]"}, - {NULL, NULL, 0, NULL} -}; - -int CmdLFT55XX(const char *Cmd) { - clearCommandBuffer(); - CmdsParse(CommandTable, Cmd); - return 0; -} - -int CmdHelp(const char *Cmd) { - CmdsHelp(CommandTable); - return 0; -} +//----------------------------------------------------------------------------- +// +// 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. +//----------------------------------------------------------------------------- +// Low frequency T55xx commands +//----------------------------------------------------------------------------- +#include "cmdlft55xx.h" + +// Default configuration +t55xx_conf_block_t config = { .modulation = DEMOD_ASK, .inverted = false, .offset = 0x00, .block0 = 0x00, .Q5 = false }; + +t55xx_conf_block_t Get_t55xx_Config(){ + return config; +} +void Set_t55xx_Config(t55xx_conf_block_t conf){ + config = conf; +} + +int usage_t55xx_config(){ + PrintAndLogEx(NORMAL, "Usage: lf t55xx config [d ] [i 1] [o ] [Q5]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - This help"); + PrintAndLogEx(NORMAL, " b <8|16|32|40|50|64|100|128> - Set bitrate"); + PrintAndLogEx(NORMAL, " d - Set demodulation FSK / ASK / PSK / NRZ / Biphase / Biphase A"); + PrintAndLogEx(NORMAL, " i [1] - Invert data signal, defaults to normal"); + PrintAndLogEx(NORMAL, " o [offset] - Set offset, where data should start decode in bitstream"); + PrintAndLogEx(NORMAL, " Q5 - Set as Q5(T5555) chip instead of T55x7"); + PrintAndLogEx(NORMAL, " ST - Set Sequence Terminator on"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx config d FSK - FSK demodulation"); + PrintAndLogEx(NORMAL, " lf t55xx config d FSK i 1 - FSK demodulation, inverse data"); + PrintAndLogEx(NORMAL, " lf t55xx config d FSK i 1 o 3 - FSK demodulation, inverse data, offset=3,start from position 3 to decode data"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_read(){ + PrintAndLogEx(NORMAL, "Usage: lf t55xx read [b ] [p ] "); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " b - block number to read. Between 0-7"); + PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); + PrintAndLogEx(NORMAL, " o - OPTIONAL override safety check"); + PrintAndLogEx(NORMAL, " 1 - OPTIONAL read Page 1 instead of Page 0"); + PrintAndLogEx(NORMAL, " ****WARNING****"); + PrintAndLogEx(NORMAL, " Use of read with password on a tag not configured for a pwd"); + PrintAndLogEx(NORMAL, " can damage the tag"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx read b 0 - read data from block 0"); + PrintAndLogEx(NORMAL, " lf t55xx read b 0 p feedbeef - read data from block 0 password feedbeef"); + PrintAndLogEx(NORMAL, " lf t55xx read b 0 p feedbeef o - read data from block 0 password feedbeef safety check"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_write(){ + PrintAndLogEx(NORMAL, "Usage: lf t55xx write [b ] [d ] [p ] [1] [t]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " b - block number to write. Between 0-7"); + PrintAndLogEx(NORMAL, " d - 4 bytes of data to write (8 hex characters)"); + PrintAndLogEx(NORMAL, " p - OPTIONAL password 4bytes (8 hex characters)"); + PrintAndLogEx(NORMAL, " 1 - OPTIONAL write Page 1 instead of Page 0"); + PrintAndLogEx(NORMAL, " t - OPTIONAL test mode write - ****DANGER****"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx write b 3 d 11223344 - write 11223344 to block 3"); + PrintAndLogEx(NORMAL, " lf t55xx write b 3 d 11223344 p feedbeef - write 11223344 to block 3 password feedbeef"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_trace() { + PrintAndLogEx(NORMAL, "Usage: lf t55xx trace [1]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx trace"); + PrintAndLogEx(NORMAL, " lf t55xx trace 1"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_info() { + PrintAndLogEx(NORMAL, "Usage: lf t55xx info [1]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx info"); + PrintAndLogEx(NORMAL, " lf t55xx info 1"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_dump(){ + PrintAndLogEx(NORMAL, "Usage: lf t55xx dump [o]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " - OPTIONAL password 4bytes (8 hex symbols)"); + PrintAndLogEx(NORMAL, " o - OPTIONAL override, force pwd read despite danger to card"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx dump"); + PrintAndLogEx(NORMAL, " lf t55xx dump feedbeef o"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_detect(){ + PrintAndLogEx(NORMAL, "Usage: lf t55xx detect [1] [p ]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); + PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx detect"); + PrintAndLogEx(NORMAL, " lf t55xx detect 1"); + PrintAndLogEx(NORMAL, " lf t55xx detect p 11223344"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_detectP1(){ + PrintAndLogEx(NORMAL, "Command: Detect Page 1 of a t55xx chip"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx p1detect [1] [p ]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " 1 - if set, use Graphbuffer otherwise read data from tag."); + PrintAndLogEx(NORMAL, " p - OPTIONAL password (8 hex characters)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx p1detect"); + PrintAndLogEx(NORMAL, " lf t55xx p1detect 1"); + PrintAndLogEx(NORMAL, " lf t55xx p1detect p 11223344"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_wakup(){ + PrintAndLogEx(NORMAL, "Usage: lf t55xx wakeup [h] p "); + PrintAndLogEx(NORMAL, "This commands send the Answer-On-Request command and leaves the readerfield ON afterwards."); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " p - password 4bytes (8 hex symbols)"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx wakeup p 11223344 - send wakeup password"); + return 0; +} +int usage_t55xx_chk(){ + PrintAndLogEx(NORMAL, "This command uses a dictionary attack"); + PrintAndLogEx(NORMAL, "press 'enter' to cancel the command"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx bruteforce [h] [i <*.dic>]"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " m - use dictionary from flashmemory\n"); + PrintAndLogEx(NORMAL, " i <*.dic> - loads a default keys dictionary file <*.dic>"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx bruteforce m"); + PrintAndLogEx(NORMAL, " lf t55xx bruteforce i default_pwd.dic"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_bruteforce(){ + PrintAndLogEx(NORMAL, "This command uses bruteforce to scan a number range"); + PrintAndLogEx(NORMAL, "press 'enter' to cancel the command"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx bruteforce [h] "); + PrintAndLogEx(NORMAL, " password must be 4 bytes (8 hex symbols)"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " - 4 byte hex value to start pwd search at"); + PrintAndLogEx(NORMAL, " - 4 byte hex value to end pwd search at"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx bruteforce aaaaaaaa bbbbbbbb"); + PrintAndLogEx(NORMAL, ""); + return 0; +} +int usage_t55xx_recoverpw(){ + PrintAndLogEx(NORMAL, "This command uses a few tricks to try to recover mangled password"); + PrintAndLogEx(NORMAL, "press 'enter' to cancel the command"); + PrintAndLogEx(NORMAL, "WARNING: this may brick non-password protected chips!"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx recoverpw [password]"); + PrintAndLogEx(NORMAL, " password must be 4 bytes (8 hex symbols)"); + PrintAndLogEx(NORMAL, " default password is 51243648, used by many cloners"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " [password] - 4 byte hex value of password written by cloner"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx recoverpw"); + PrintAndLogEx(NORMAL, " lf t55xx recoverpw 51243648"); + PrintAndLogEx(NORMAL, ""); + return 0; +}int usage_t55xx_wipe(){ + PrintAndLogEx(NORMAL, "Usage: lf t55xx wipe [h] [Q5]"); + PrintAndLogEx(NORMAL, "This commands wipes a tag, fills blocks 1-7 with zeros and a default configuration block"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - this help"); + PrintAndLogEx(NORMAL, " Q5 - indicates to use the T5555 (Q5) default configuration block"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx wipe - wipes a t55x7 tag, config block 0x000880E0"); + PrintAndLogEx(NORMAL, " lf t55xx wipe Q5 - wipes a t5555 Q5 tag, config block 0x6001F004"); + return 0; +} +int usage_lf_deviceconfig(){ + PrintAndLogEx(NORMAL, "Sets t55x7 timings for direkt commands. The timings are set here in Field Clocks (FC), \nwhich is converted to (US) on device"); + PrintAndLogEx(NORMAL, "Usage: lf t55xx deviceconfig a b c d e p"); + PrintAndLogEx(NORMAL, "Options:"); + PrintAndLogEx(NORMAL, " h - This help"); + PrintAndLogEx(NORMAL, " a <8..255> - Set start gap"); + PrintAndLogEx(NORMAL, " b <8..255> - Set write gap"); + PrintAndLogEx(NORMAL, " c <8..255> - Set write ZERO gap"); + PrintAndLogEx(NORMAL, " d <8..255> - Set write ONE gap"); + PrintAndLogEx(NORMAL, " e <8..255> - Set read gap"); + PrintAndLogEx(NORMAL, " p - persist to flashmemory"); + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "Examples:"); + PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 29 b 17 c 15 d 47 e 15 - default T55XX"); + PrintAndLogEx(NORMAL, " lf t55xx deviceconfig a 55 b 14 c 21 d 30 - default EM4305"); + PrintAndLogEx(NORMAL, ""); + return 0; +} + +int CmdHelp(const char *Cmd); + +void printT5xxHeader(uint8_t page){ + PrintAndLogEx(NORMAL, "Reading Page %d:", page); + PrintAndLogEx(NORMAL, "blk | hex data | binary | ascii"); + PrintAndLogEx(NORMAL, "----+----------+----------------------------------+-------"); +} + +int CmdT55xxSetConfig(const char *Cmd) { + + uint8_t offset = 0; + char modulation[6] = {0x00}; + char tmp = 0x00; + uint8_t bitRate = 0; + uint8_t rates[9] = {8,16,32,40,50,64,100,128,0}; + uint8_t cmdp = 0; + bool errors = false; + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + tmp = tolower(param_getchar(Cmd, cmdp)); + switch(tmp) { + case 'h': + return usage_t55xx_config(); + case 'b': + errors |= param_getdec(Cmd, cmdp+1, &bitRate); + if ( !errors){ + uint8_t i = 0; + for (; i < 9; i++){ + if (rates[i]==bitRate) { + config.bitrate = i; + break; + } + } + if (i==9) errors = true; + } + cmdp+=2; + break; + case 'd': + param_getstr(Cmd, cmdp+1, modulation, sizeof(modulation)); + cmdp += 2; + + if ( strcmp(modulation, "FSK" ) == 0) { + config.modulation = DEMOD_FSK; + } else if ( strcmp(modulation, "FSK1" ) == 0) { + config.modulation = DEMOD_FSK1; + config.inverted=1; + } else if ( strcmp(modulation, "FSK1a" ) == 0) { + config.modulation = DEMOD_FSK1a; + config.inverted=0; + } else if ( strcmp(modulation, "FSK2" ) == 0) { + config.modulation = DEMOD_FSK2; + config.inverted=0; + } else if ( strcmp(modulation, "FSK2a" ) == 0) { + config.modulation = DEMOD_FSK2a; + config.inverted=1; + } else if ( strcmp(modulation, "ASK" ) == 0) { + config.modulation = DEMOD_ASK; + } else if ( strcmp(modulation, "NRZ" ) == 0) { + config.modulation = DEMOD_NRZ; + } else if ( strcmp(modulation, "PSK1" ) == 0) { + config.modulation = DEMOD_PSK1; + } else if ( strcmp(modulation, "PSK2" ) == 0) { + config.modulation = DEMOD_PSK2; + } else if ( strcmp(modulation, "PSK3" ) == 0) { + config.modulation = DEMOD_PSK3; + } else if ( strcmp(modulation, "BIa" ) == 0) { + config.modulation = DEMOD_BIa; + config.inverted=1; + } else if ( strcmp(modulation, "BI" ) == 0) { + config.modulation = DEMOD_BI; + config.inverted=0; + } else { + PrintAndLogEx(WARNING, "Unknown modulation '%s'", modulation); + errors = true; + } + break; + case 'i': + config.inverted = param_getchar(Cmd,cmdp+1) == '1'; + cmdp+=2; + break; + case 'o': + errors |= param_getdec(Cmd, cmdp+1, &offset); + if ( !errors ) + config.offset = offset; + cmdp+=2; + break; + case 'q': + config.Q5 = true; + cmdp++; + break; + case 's': + config.ST = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + + // No args + if (cmdp == 0) return printConfiguration( config ); + + //Validations + if (errors) return usage_t55xx_config(); + + config.block0 = 0; + return printConfiguration ( config ); +} + +int T55xxReadBlock(uint8_t block, bool page1, bool usepwd, bool override, uint32_t password){ + //Password mode + if ( usepwd ) { + // try reading the config block and verify that PWD bit is set before doing this! + if ( !override ) { + + if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, false, 0 ) ) return 0; + + if ( !tryDetectModulation() ) { + PrintAndLogEx(NORMAL, "Safety Check: Could not detect if PWD bit is set in config block. Exits."); + return 0; + } else { + PrintAndLogEx(NORMAL, "Safety Check: PWD bit is NOT set in config block. Reading without password..."); + usepwd = false; + page1 = false; + } + } else { + PrintAndLogEx(NORMAL, "Safety Check Overriden - proceeding despite risk"); + } + } + + if (!AquireData(page1, block, usepwd, password) ) return 0; + if (!DecodeT55xxBlock()) return 0; + + char blk[10] = {0}; + sprintf(blk, "%02d", block); + printT55xxBlock(blk); + return 1; +} + +int CmdT55xxReadBlock(const char *Cmd) { + uint8_t block = REGULAR_READ_MODE_BLOCK; + uint32_t password = 0; //default to blank Block 7 + bool usepwd = false; + bool override = false; + bool page1 = false; + bool errors = false; + uint8_t cmdp = 0; + while(param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch ( tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_t55xx_read(); + case 'b': + errors |= param_getdec(Cmd, cmdp+1, &block); + cmdp += 2; + break; + case 'o': + override = true; + cmdp++; + break; + case 'p': + password = param_get32ex(Cmd, cmdp+1, 0, 16); + usepwd = true; + cmdp += 2; + break; + case '1': + page1 = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (errors) return usage_t55xx_read(); + + if (block > 7 && block != REGULAR_READ_MODE_BLOCK ) { + PrintAndLogEx(NORMAL, "Block must be between 0 and 7"); + return 0; + } + + printT5xxHeader(page1); + return T55xxReadBlock(block, page1, usepwd, override, password); +} + +bool DecodeT55xxBlock(){ + + char buf[30] = {0x00}; + char *cmdStr = buf; + int ans = 0; + bool ST = config.ST; + uint8_t bitRate[8] = {8,16,32,40,50,64,100,128}; + DemodBufferLen = 0x00; + + switch( config.modulation ){ + case DEMOD_FSK: + snprintf(cmdStr, sizeof(buf),"%d %d", bitRate[config.bitrate], config.inverted ); + ans = FSKrawDemod(cmdStr, false); + break; + case DEMOD_FSK1: + case DEMOD_FSK1a: + snprintf(cmdStr, sizeof(buf),"%d %d 8 5", bitRate[config.bitrate], config.inverted ); + ans = FSKrawDemod(cmdStr, false); + break; + case DEMOD_FSK2: + case DEMOD_FSK2a: + snprintf(cmdStr, sizeof(buf),"%d %d 10 8", bitRate[config.bitrate], config.inverted ); + ans = FSKrawDemod(cmdStr, false); + break; + case DEMOD_ASK: + snprintf(cmdStr, sizeof(buf),"%d %d 1", bitRate[config.bitrate], config.inverted ); + ans = ASKDemod_ext(cmdStr, false, false, 1, &ST); + break; + case DEMOD_PSK1: + // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) + save_restoreGB(GRAPH_SAVE); + CmdLtrim("160"); + snprintf(cmdStr, sizeof(buf),"%d %d 6", bitRate[config.bitrate], config.inverted ); + ans = PSKDemod(cmdStr, false); + //undo trim samples + save_restoreGB(GRAPH_RESTORE); + break; + case DEMOD_PSK2: //inverted won't affect this + case DEMOD_PSK3: //not fully implemented + // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) + save_restoreGB(GRAPH_SAVE); + CmdLtrim("160"); + snprintf(cmdStr, sizeof(buf),"%d 0 6", bitRate[config.bitrate] ); + ans = PSKDemod(cmdStr, false); + psk1TOpsk2(DemodBuffer, DemodBufferLen); + //undo trim samples + save_restoreGB(GRAPH_RESTORE); + break; + case DEMOD_NRZ: + snprintf(cmdStr, sizeof(buf),"%d %d 1", bitRate[config.bitrate], config.inverted ); + ans = NRZrawDemod(cmdStr, false); + break; + case DEMOD_BI: + case DEMOD_BIa: + snprintf(cmdStr, sizeof(buf),"0 %d %d 1", bitRate[config.bitrate], config.inverted ); + ans = ASKbiphaseDemod(cmdStr, false); + break; + default: + return false; + } + return (bool) ans; +} + +bool DecodeT5555TraceBlock() { + DemodBufferLen = 0x00; + + // According to datasheet. Always: RF/64, not inverted, Manchester + return (bool) ASKDemod("64 0 1", false, false, 1); +} + +// sanity check. Don't use proxmark if it is offline and you didn't specify useGraphbuf +static int SanityOfflineCheck( bool useGraphBuffer ){ + if ( !useGraphBuffer && IsOffline() ) { + PrintAndLogEx(NORMAL, "Your proxmark3 device is offline. Specify [1] to use graphbuffer data instead"); + return 0; + } + return 1; +} + +int CmdT55xxDetect(const char *Cmd){ + bool errors = false; + bool useGB = false, usepwd = false; + uint32_t password = 0; + uint8_t cmdp = 0; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch ( tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_t55xx_detect(); + case 'p': + password = param_get32ex(Cmd, cmdp+1, 0, 16); + usepwd = true; + cmdp += 2; + break; + case '1': + // use Graphbuffer data + useGB = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (errors) return usage_t55xx_detect(); + + // sanity check. + if (!SanityOfflineCheck(useGB)) return 1; + + if ( !useGB) { + if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, usepwd, password) ) + return 1; + } + + if ( !tryDetectModulation() ) + PrintAndLogEx(WARNING, "Could not detect modulation automatically. Try setting it manually with \'lf t55xx config\'"); + + return 0; +} + +// detect configuration? +bool tryDetectModulation(){ + + t55xx_conf_block_t tests[15]; + int bitRate = 0, clk = 0, firstClockEdge = 0; + uint8_t hits = 0, fc1 = 0, fc2 = 0, ans = 0; + + ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); + + if (ans && ((fc1==10 && fc2==8) || (fc1==8 && fc2==5))) { + if ( FSKrawDemod("0 0", false) && test(DEMOD_FSK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)){ + tests[hits].modulation = DEMOD_FSK; + if (fc1==8 && fc2 == 5) + tests[hits].modulation = DEMOD_FSK1a; + else if (fc1==10 && fc2 == 8) + tests[hits].modulation = DEMOD_FSK2; + tests[hits].bitrate = bitRate; + tests[hits].inverted = false; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + if ( FSKrawDemod("0 1", false) && test(DEMOD_FSK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { + tests[hits].modulation = DEMOD_FSK; + if (fc1 == 8 && fc2 == 5) + tests[hits].modulation = DEMOD_FSK1; + else if (fc1 == 10 && fc2 == 8) + tests[hits].modulation = DEMOD_FSK2a; + tests[hits].bitrate = bitRate; + tests[hits].inverted = true; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + } else { + clk = GetAskClock("", false); + if (clk > 0) { + tests[hits].ST = true; + // "0 0 1 " == clock auto, invert false, maxError 1. + // false = no verbose + // false = no emSearch + // 1 = Ask/Man + // st = true + if ( ASKDemod_ext("0 0 1", false, false, 1, &tests[hits].ST) && test(DEMOD_ASK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { + tests[hits].modulation = DEMOD_ASK; + tests[hits].bitrate = bitRate; + tests[hits].inverted = false; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + ++hits; + } + tests[hits].ST = true; + // "0 0 1 " == clock auto, invert true, maxError 1. + // false = no verbose + // false = no emSearch + // 1 = Ask/Man + // st = true + if ( ASKDemod_ext("0 1 1", false, false, 1, &tests[hits].ST) && test(DEMOD_ASK, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { + tests[hits].modulation = DEMOD_ASK; + tests[hits].bitrate = bitRate; + tests[hits].inverted = true; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + ++hits; + } + if ( ASKbiphaseDemod("0 0 0 2", false) && test(DEMOD_BI, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5) ) { + tests[hits].modulation = DEMOD_BI; + tests[hits].bitrate = bitRate; + tests[hits].inverted = false; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + if ( ASKbiphaseDemod("0 0 1 2", false) && test(DEMOD_BIa, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5) ) { + tests[hits].modulation = DEMOD_BIa; + tests[hits].bitrate = bitRate; + tests[hits].inverted = true; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + } + clk = GetNrzClock("", false); + if (clk > 8) { //clock of rf/8 is likely a false positive, so don't use it. + if ( NRZrawDemod("0 0 1", false) && test(DEMOD_NRZ, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { + tests[hits].modulation = DEMOD_NRZ; + tests[hits].bitrate = bitRate; + tests[hits].inverted = false; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + + if ( NRZrawDemod("0 1 1", false) && test(DEMOD_NRZ, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { + tests[hits].modulation = DEMOD_NRZ; + tests[hits].bitrate = bitRate; + tests[hits].inverted = true; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + } + + clk = GetPskClock("", false); + if (clk > 0) { + // allow undo + save_restoreGB(GRAPH_SAVE); + // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) + CmdLtrim("160"); + if ( PSKDemod("0 0 6", false) && test(DEMOD_PSK1, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { + tests[hits].modulation = DEMOD_PSK1; + tests[hits].bitrate = bitRate; + tests[hits].inverted = false; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + if ( PSKDemod("0 1 6", false) && test(DEMOD_PSK1, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)) { + tests[hits].modulation = DEMOD_PSK1; + tests[hits].bitrate = bitRate; + tests[hits].inverted = true; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + //ICEMAN: are these PSKDemod calls needed? + // PSK2 - needs a call to psk1TOpsk2. + if ( PSKDemod("0 0 6", false)) { + psk1TOpsk2(DemodBuffer, DemodBufferLen); + if (test(DEMOD_PSK2, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)){ + tests[hits].modulation = DEMOD_PSK2; + tests[hits].bitrate = bitRate; + tests[hits].inverted = false; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + } // inverse waves does not affect this demod + // PSK3 - needs a call to psk1TOpsk2. + if ( PSKDemod("0 0 6", false)) { + psk1TOpsk2(DemodBuffer, DemodBufferLen); + if (test(DEMOD_PSK3, &tests[hits].offset, &bitRate, clk, &tests[hits].Q5)){ + tests[hits].modulation = DEMOD_PSK3; + tests[hits].bitrate = bitRate; + tests[hits].inverted = false; + tests[hits].block0 = PackBits(tests[hits].offset, 32, DemodBuffer); + tests[hits].ST = false; + ++hits; + } + } // inverse waves does not affect this demod + //undo trim samples + save_restoreGB(GRAPH_RESTORE); + } + } + if ( hits == 1) { + config.modulation = tests[0].modulation; + config.bitrate = tests[0].bitrate; + config.inverted = tests[0].inverted; + config.offset = tests[0].offset; + config.block0 = tests[0].block0; + config.Q5 = tests[0].Q5; + config.ST = tests[0].ST; + printConfiguration( config ); + return true; + } + + bool retval = false; + if ( hits > 1) { + PrintAndLogEx(SUCCESS, "Found [%d] possible matches for modulation.", hits); + for(int i=0; i= DEMOD_FSK1 && modread <= DEMOD_FSK2a) return true; + break; + case DEMOD_ASK: + if (modread == DEMOD_ASK) return true; + break; + case DEMOD_PSK1: + if (modread == DEMOD_PSK1) return true; + break; + case DEMOD_PSK2: + if (modread == DEMOD_PSK2) return true; + break; + case DEMOD_PSK3: + if (modread == DEMOD_PSK3) return true; + break; + case DEMOD_NRZ: + if (modread == DEMOD_NRZ) return true; + break; + case DEMOD_BI: + if (modread == DEMOD_BI) return true; + break; + case DEMOD_BIa: + if (modread == DEMOD_BIa) return true; + break; + default: + return false; + } + return false; +} + +bool testQ5Modulation(uint8_t mode, uint8_t modread){ + switch( mode ){ + case DEMOD_FSK: + if (modread >= 4 && modread <= 5) return true; + break; + case DEMOD_ASK: + if (modread == 0) return true; + break; + case DEMOD_PSK1: + if (modread == 1) return true; + break; + case DEMOD_PSK2: + if (modread == 2) return true; + break; + case DEMOD_PSK3: + if (modread == 3) return true; + break; + case DEMOD_NRZ: + if (modread == 7) return true; + break; + case DEMOD_BI: + if (modread == 6) return true; + break; + default: + return false; + } + return false; +} + +int convertQ5bitRate(uint8_t bitRateRead) { + uint8_t expected[] = {8, 16, 32, 40, 50, 64, 100, 128}; + for (int i=0; i<8; i++) + if (expected[i] == bitRateRead) + return i; + + return -1; +} + +bool testQ5(uint8_t mode, uint8_t *offset, int *fndBitRate, uint8_t clk){ + + if ( DemodBufferLen < 64 ) return false; + uint8_t si = 0; + for (uint8_t idx = 28; idx < 64; idx++){ + si = idx; + if ( PackBits(si, 28, DemodBuffer) == 0x00 ) continue; + + uint8_t safer = PackBits(si, 4, DemodBuffer); si += 4; //master key + uint8_t resv = PackBits(si, 8, DemodBuffer); si += 8; + // 2nibble must be zeroed. + if (safer != 0x6 && safer != 0x9) continue; + if ( resv > 0x00) continue; + //uint8_t pageSel = PackBits(si, 1, DemodBuffer); si += 1; + //uint8_t fastWrite = PackBits(si, 1, DemodBuffer); si += 1; + si += 1+1; + int bitRate = PackBits(si, 6, DemodBuffer)*2 + 2; si += 6; //bit rate + if (bitRate > 128 || bitRate < 8) continue; + + //uint8_t AOR = PackBits(si, 1, DemodBuffer); si += 1; + //uint8_t PWD = PackBits(si, 1, DemodBuffer); si += 1; + //uint8_t pskcr = PackBits(si, 2, DemodBuffer); si += 2; //could check psk cr + //uint8_t inverse = PackBits(si, 1, DemodBuffer); si += 1; + si += 1+1+2+1; + uint8_t modread = PackBits(si, 3, DemodBuffer); si += 3; + uint8_t maxBlk = PackBits(si, 3, DemodBuffer); si += 3; + //uint8_t ST = PackBits(si, 1, DemodBuffer); si += 1; + if (maxBlk == 0) continue; + //test modulation + if (!testQ5Modulation(mode, modread)) continue; + if (bitRate != clk) continue; + *fndBitRate = convertQ5bitRate(bitRate); + if (*fndBitRate < 0) continue; + *offset = idx; + + return true; + } + return false; +} + +bool testBitRate(uint8_t readRate, uint8_t clk){ + uint8_t expected[] = {8, 16, 32, 40, 50, 64, 100, 128}; + if (expected[readRate] == clk) + return true; + + return false; +} + +bool test(uint8_t mode, uint8_t *offset, int *fndBitRate, uint8_t clk, bool *Q5){ + + if ( DemodBufferLen < 64 ) return false; + uint8_t si = 0; + for (uint8_t idx = 28; idx < 64; idx++){ + si = idx; + if ( PackBits(si, 28, DemodBuffer) == 0x00 ) continue; + + uint8_t safer = PackBits(si, 4, DemodBuffer); si += 4; //master key + uint8_t resv = PackBits(si, 4, DemodBuffer); si += 4; //was 7 & +=7+3 //should be only 4 bits if extended mode + // 2nibble must be zeroed. + // moved test to here, since this gets most faults first. + if ( resv > 0x00) continue; + + int bitRate = PackBits(si, 6, DemodBuffer); si += 6; //bit rate (includes extended mode part of rate) + uint8_t extend = PackBits(si, 1, DemodBuffer); si += 1; //bit 15 extended mode + uint8_t modread = PackBits(si, 5, DemodBuffer); si += 5+2+1; + //uint8_t pskcr = PackBits(si, 2, DemodBuffer); si += 2+1; //could check psk cr + //uint8_t nml01 = PackBits(si, 1, DemodBuffer); si += 1+5; //bit 24, 30, 31 could be tested for 0 if not extended mode + //uint8_t nml02 = PackBits(si, 2, DemodBuffer); si += 2; + + //if extended mode + bool extMode =( (safer == 0x6 || safer == 0x9) && extend) ? true : false; + + if (!extMode){ + if (bitRate > 7) continue; + if (!testBitRate(bitRate, clk)) continue; + } else { //extended mode bitrate = same function to calc bitrate as em4x05 + if (EM4x05_GET_BITRATE(bitRate) != clk) continue; + + } + //test modulation + if (!testModulation(mode, modread)) continue; + *fndBitRate = bitRate; + *offset = idx; + *Q5 = false; + return true; + } + if (testQ5(mode, offset, fndBitRate, clk)) { + *Q5 = true; + return true; + } + return false; +} + +void printT55xxBlock(const char *blockNum){ + + uint8_t i = config.offset; + uint8_t endpos = 32 + i; + uint32_t blockData = 0; + uint8_t bits[64] = {0x00}; + + if ( !DemodBufferLen) return; + + if ( endpos > DemodBufferLen){ + PrintAndLogEx(NORMAL, "The configured offset %d is too big. Possible offset: %d)", i, DemodBufferLen-32); + return; + } + + for (; i < endpos; ++i) + bits[i - config.offset] = DemodBuffer[i]; + + blockData = PackBits(0, 32, bits); + uint8_t bytes[4] = {0}; + num_to_bytes(blockData, 4, bytes); + + PrintAndLogEx(NORMAL, " %s | %08X | %s | %s", blockNum, blockData, sprint_bin(bits,32), sprint_ascii(bytes,4)); +} + +int special(const char *Cmd) { + uint32_t blockData = 0; + uint8_t bits[32] = {0x00}; + + PrintAndLogEx(NORMAL, "OFFSET | DATA | BINARY | ASCII"); + PrintAndLogEx(NORMAL, "-------+-------+-------------------------------------+------"); + int i,j = 0; + for (; j < 64; ++j){ + + for (i = 0; i < 32; ++i) + bits[i]=DemodBuffer[j+i]; + + blockData = PackBits(0, 32, bits); + + PrintAndLogEx(NORMAL, "%02d | 0x%08X | %s",j , blockData, sprint_bin(bits,32)); + } + return 0; +} + +int printConfiguration( t55xx_conf_block_t b){ + PrintAndLogEx(NORMAL, "Chip Type : %s", (b.Q5) ? "T5555(Q5)" : "T55x7"); + PrintAndLogEx(NORMAL, "Modulation : %s", GetSelectedModulationStr(b.modulation) ); + PrintAndLogEx(NORMAL, "Bit Rate : %s", GetBitRateStr(b.bitrate, (b.block0 & T55x7_X_MODE && (b.block0>>28==6 || b.block0>>28==9))) ); + PrintAndLogEx(NORMAL, "Inverted : %s", (b.inverted) ? _GREEN_(Yes) : "No" ); + PrintAndLogEx(NORMAL, "Offset : %d", b.offset); + PrintAndLogEx(NORMAL, "Seq. Term. : %s", (b.ST) ? _GREEN_(Yes) : "No" ); + PrintAndLogEx(NORMAL, "Block0 : 0x%08X", b.block0); + PrintAndLogEx(NORMAL, ""); + return 0; +} + +int CmdT55xxWakeUp(const char *Cmd) { + uint32_t password = 0; + uint8_t cmdp = 0; + bool errors = false; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_t55xx_wakup(); + case 'p': + password = param_get32ex(Cmd, cmdp+1, 0, 16); + cmdp += 2; + errors = false; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (errors) return usage_t55xx_wakup(); + + UsbCommand c = {CMD_T55XX_WAKEUP, {password, 0, 0}}; + clearCommandBuffer(); + SendCommand(&c); + PrintAndLogEx(SUCCESS, "Wake up command sent. Try read now"); + return 0; +} + +int CmdT55xxWriteBlock(const char *Cmd) { + uint8_t block = 0xFF; //default to invalid block + uint32_t data = 0; //default to blank Block + uint32_t password = 0; //default to blank Block 7 + bool usepwd = false; + bool page1 = false; + bool gotdata = false; + bool testMode = false; + bool errors = false; + uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_t55xx_write(); + case 'b': + errors |= param_getdec(Cmd, cmdp+1, &block); + cmdp += 2; + break; + case 'd': + data = param_get32ex(Cmd, cmdp+1, 0, 16); + gotdata = true; + cmdp += 2; + break; + case 'p': + password = param_get32ex(Cmd, cmdp+1, 0, 16); + usepwd = true; + cmdp += 2; + break; + case 't': + testMode = true; + cmdp++; + break; + case '1': + page1 = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (errors || !gotdata) return usage_t55xx_write(); + + if (block > 7) { + PrintAndLogEx(WARNING, "Block number must be between 0 and 7"); + return 0; + } + + UsbCommand c = {CMD_T55XX_WRITE_BLOCK, {data, block, 0}}; + UsbCommand resp; + c.d.asBytes[0] = (page1) ? 0x2 : 0; + c.d.asBytes[0] |= (testMode) ? 0x4 : 0; + + char pwdStr[16] = {0}; + snprintf(pwdStr, sizeof(pwdStr), "pwd: 0x%08X", password); + + PrintAndLogEx(INFO, "Writing page %d block: %02d data: 0x%08X %s", page1, block, data, (usepwd) ? pwdStr : "" ); + + //Password mode + if (usepwd) { + c.arg[2] = password; + c.d.asBytes[0] |= 0x1; + } + clearCommandBuffer(); + SendCommand(&c); + if ( !WaitForResponseTimeout(CMD_ACK, &resp, 1500 )){ + PrintAndLogEx(WARNING, "Error occurred, device did not ACK write operation. (May be due to old firmware)"); + return 0; + } + return 1; +} + +int CmdT55xxReadTrace(const char *Cmd) { + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) > 1 || cmdp == 'h') return usage_t55xx_trace(); + + bool pwdmode = false; + uint32_t password = 0; + + if (strlen(Cmd) == 0) { + // sanity check. + if (!SanityOfflineCheck(false)) return 1; + + if ( !AquireData( T55x7_PAGE1, REGULAR_READ_MODE_BLOCK, pwdmode, password ) ) + return 1; + } + + if ( config.Q5 ){ + if (!DecodeT5555TraceBlock()) return 1; + } else { + if (!DecodeT55xxBlock()) return 1; + } + + if ( !DemodBufferLen ) return 1; + + RepaintGraphWindow(); + uint8_t repeat = (config.offset > 5) ? 32 : 0; + + uint8_t si = config.offset + repeat; + uint32_t bl1 = PackBits(si, 32, DemodBuffer); + uint32_t bl2 = PackBits(si+32, 32, DemodBuffer); + + if (config.Q5) { + uint32_t hdr = PackBits(si, 9, DemodBuffer); si += 9; + + if (hdr != 0x1FF) { + PrintAndLogEx(FAILED, "Invalid Q5 Trace data header (expected 0x1FF, found %X)", hdr); + return 1; + } + + t5555_tracedata_t data = {.bl1 = bl1, .bl2 = bl2, .icr = 0, .lotidc = '?', .lotid = 0, .wafer = 0, .dw =0}; + + data.icr = PackBits(si, 2, DemodBuffer); si += 2; + data.lotidc = 'Z' - PackBits(si, 2, DemodBuffer); si += 3; + + data.lotid = PackBits(si, 4, DemodBuffer); si += 5; + data.lotid <<= 4; + data.lotid |= PackBits(si, 4, DemodBuffer); si += 5; + data.lotid <<= 4; + data.lotid |= PackBits(si, 4, DemodBuffer); si += 5; + data.lotid <<= 4; + data.lotid |= PackBits(si, 4, DemodBuffer); si += 5; + data.lotid <<= 1; + data.lotid |= PackBits(si, 1, DemodBuffer); si += 1; + + data.wafer = PackBits(si, 3, DemodBuffer); si += 4; + data.wafer <<= 2; + data.wafer |= PackBits(si, 2, DemodBuffer); si += 2; + + data.dw = PackBits(si, 2, DemodBuffer); si += 3; + data.dw <<= 4; + data.dw |= PackBits(si, 4, DemodBuffer); si += 5; + data.dw <<= 4; + data.dw |= PackBits(si, 4, DemodBuffer); si += 5; + data.dw <<= 4; + data.dw |= PackBits(si, 4, DemodBuffer); si += 5; + + printT5555Trace(data, repeat); + + } else { + + t55x7_tracedata_t data = {.bl1 = bl1, .bl2 = bl2, .acl = 0, .mfc = 0, .cid = 0, .year = 0, .quarter = 0, .icr = 0, .lotid = 0, .wafer = 0, .dw = 0}; + + data.acl = PackBits(si, 8, DemodBuffer); si += 8; + if ( data.acl != 0xE0 ) { + PrintAndLogEx(FAILED, "The modulation is most likely wrong since the ACL is not 0xE0. "); + return 1; + } + + data.mfc = PackBits(si, 8, DemodBuffer); si += 8; + data.cid = PackBits(si, 5, DemodBuffer); si += 5; + data.icr = PackBits(si, 3, DemodBuffer); si += 3; + data.year = PackBits(si, 4, DemodBuffer); si += 4; + data.quarter = PackBits(si, 2, DemodBuffer); si += 2; + data.lotid = PackBits(si, 14, DemodBuffer); si += 14; + data.wafer = PackBits(si, 5, DemodBuffer); si += 5; + data.dw = PackBits(si, 15, DemodBuffer); + + time_t t = time(NULL); + struct tm tm = *localtime(&t); + if ( data.year > tm.tm_year-110) + data.year += 2000; + else + data.year += 2010; + + printT55x7Trace(data, repeat); + } + return 0; +} + +void printT55x7Trace( t55x7_tracedata_t data, uint8_t repeat ){ + PrintAndLogEx(NORMAL, "-- T55x7 Trace Information ----------------------------------"); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + PrintAndLogEx(NORMAL, " ACL Allocation class (ISO/IEC 15963-1) : 0x%02X (%d)", data.acl, data.acl); + PrintAndLogEx(NORMAL, " MFC Manufacturer ID (ISO/IEC 7816-6) : 0x%02X (%d) - %s", data.mfc, data.mfc, getTagInfo(data.mfc)); + PrintAndLogEx(NORMAL, " CID : 0x%02X (%d) - %s", data.cid, data.cid, GetModelStrFromCID(data.cid)); + PrintAndLogEx(NORMAL, " ICR IC Revision : %d", data.icr ); + PrintAndLogEx(NORMAL, " Manufactured"); + PrintAndLogEx(NORMAL, " Year/Quarter : %d/%d", data.year, data.quarter); + PrintAndLogEx(NORMAL, " Lot ID : %d", data.lotid ); + PrintAndLogEx(NORMAL, " Wafer number : %d", data.wafer); + PrintAndLogEx(NORMAL, " Die Number : %d", data.dw); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + PrintAndLogEx(NORMAL, " Raw Data - Page 1"); + PrintAndLogEx(NORMAL, " Block 1 : 0x%08X %s", data.bl1, sprint_bin(DemodBuffer+config.offset+repeat,32) ); + PrintAndLogEx(NORMAL, " Block 2 : 0x%08X %s", data.bl2, sprint_bin(DemodBuffer+config.offset+repeat+32,32) ); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + + /* + TRACE - BLOCK O + Bits Definition HEX + 1-8 ACL Allocation class (ISO/IEC 15963-1) 0xE0 + 9-16 MFC Manufacturer ID (ISO/IEC 7816-6) 0x15 Atmel Corporation + 17-21 CID 0x1 = Atmel ATA5577M1 0x2 = Atmel ATA5577M2 + 22-24 ICR IC revision + 25-28 YEAR (BCD encoded) 9 (= 2009) + 29-30 QUARTER 1,2,3,4 + 31-32 LOT ID + + TRACE - BLOCK 1 + 1-12 LOT ID + 13-17 Wafer number + 18-32 DW, die number sequential + */ +} + +void printT5555Trace( t5555_tracedata_t data, uint8_t repeat ){ + PrintAndLogEx(NORMAL, "-- T5555 (Q5) Trace Information -----------------------------"); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + PrintAndLogEx(NORMAL, " ICR IC Revision : %d", data.icr ); + PrintAndLogEx(NORMAL, " Lot : %c%d", data.lotidc, data.lotid); + PrintAndLogEx(NORMAL, " Wafer number : %d", data.wafer); + PrintAndLogEx(NORMAL, " Die Number : %d", data.dw); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + PrintAndLogEx(NORMAL, " Raw Data - Page 1"); + PrintAndLogEx(NORMAL, " Block 1 : 0x%08X %s", data.bl1, sprint_bin(DemodBuffer+config.offset+repeat,32) ); + PrintAndLogEx(NORMAL, " Block 2 : 0x%08X %s", data.bl2, sprint_bin(DemodBuffer+config.offset+repeat+32,32) ); + + /* + ** Q5 ** + TRACE - BLOCK O and BLOCK1 + Bits Definition HEX + 1-9 Header 0x1FF + 10-11 IC Revision + 12-13 Lot ID char + 15-35 Lot ID (NB parity) + 36-41 Wafer number (NB parity) + 42-58 DW, die number sequential (NB parity) + 60-63 Parity bits + 64 Always zero + */ +} + +//need to add Q5 info... +int CmdT55xxInfo(const char *Cmd){ + /* + Page 0 Block 0 Configuration data. + Normal mode + Extended mode + */ + bool pwdmode = false; + uint32_t password = 0; + char cmdp = tolower(param_getchar(Cmd, 0)); + + if (strlen(Cmd) > 1 || cmdp == 'h') return usage_t55xx_info(); + + if (strlen(Cmd) == 0) { + // sanity check. + if (!SanityOfflineCheck(false)) return 1; + + if ( !AquireData( T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, pwdmode, password ) ) + return 1; + } + + if (!DecodeT55xxBlock()) return 1; + + // too little space to start with + if ( DemodBufferLen < 32) return 1; + + // + //PrintAndLogEx(NORMAL, "Offset+32 ==%d\n DemodLen == %d", config.offset + 32, DemodBufferLen); + + uint8_t si = config.offset; + uint32_t block0 = PackBits(si, 32, DemodBuffer); + uint32_t safer = PackBits(si, 4, DemodBuffer); si += 4; + uint32_t resv = PackBits(si, 7, DemodBuffer); si += 7; + uint32_t dbr = PackBits(si, 3, DemodBuffer); si += 3; + uint32_t extend = PackBits(si, 1, DemodBuffer); si += 1; + uint32_t datamod = PackBits(si, 5, DemodBuffer); si += 5; + uint32_t pskcf = PackBits(si, 2, DemodBuffer); si += 2; + uint32_t aor = PackBits(si, 1, DemodBuffer); si += 1; + uint32_t otp = PackBits(si, 1, DemodBuffer); si += 1; + uint32_t maxblk = PackBits(si, 3, DemodBuffer); si += 3; + uint32_t pwd = PackBits(si, 1, DemodBuffer); si += 1; + uint32_t sst = PackBits(si, 1, DemodBuffer); si += 1; + uint32_t fw = PackBits(si, 1, DemodBuffer); si += 1; + uint32_t inv = PackBits(si, 1, DemodBuffer); si += 1; + uint32_t por = PackBits(si, 1, DemodBuffer); si += 1; + + if (config.Q5) + PrintAndLogEx(NORMAL, _RED_(*** Warning ***) " Config Info read off a Q5 will not display as expected"); + + PrintAndLogEx(NORMAL, ""); + PrintAndLogEx(NORMAL, "-- T55x7 Configuration & Tag Information --------------------"); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + PrintAndLogEx(NORMAL, " Safer key : %s", GetSaferStr(safer)); + PrintAndLogEx(NORMAL, " reserved : %d", resv); + PrintAndLogEx(NORMAL, " Data bit rate : %s", GetBitRateStr(dbr, extend)); + PrintAndLogEx(NORMAL, " eXtended mode : %s", (extend) ? _YELLOW_(Yes - Warning) : "No"); + PrintAndLogEx(NORMAL, " Modulation : %s", GetModulationStr(datamod)); + PrintAndLogEx(NORMAL, " PSK clock frequency : %d", pskcf); + PrintAndLogEx(NORMAL, " AOR - Answer on Request : %s", (aor) ? _GREEN_(Yes) : "No"); + PrintAndLogEx(NORMAL, " OTP - One Time Pad : %s", (otp) ? _YELLOW_(Yes - Warning) : "No" ); + PrintAndLogEx(NORMAL, " Max block : %d", maxblk); + PrintAndLogEx(NORMAL, " Password mode : %s", (pwd) ? _GREEN_(Yes) : "No"); + PrintAndLogEx(NORMAL, " Sequence Start Terminator : %s", (sst) ? _GREEN_(Yes) : "No"); + PrintAndLogEx(NORMAL, " Fast Write : %s", (fw) ? _GREEN_(Yes) : "No"); + PrintAndLogEx(NORMAL, " Inverse data : %s", (inv) ? _GREEN_(Yes) : "No"); + PrintAndLogEx(NORMAL, " POR-Delay : %s", (por) ? _GREEN_(Yes) : "No"); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + PrintAndLogEx(NORMAL, " Raw Data - Page 0"); + PrintAndLogEx(NORMAL, " Block 0 : 0x%08X %s", block0, sprint_bin(DemodBuffer + config.offset, 32) ); + PrintAndLogEx(NORMAL, "-------------------------------------------------------------"); + return 0; +} + +int CmdT55xxDump(const char *Cmd){ + + uint32_t password = 0; + bool override = false; + char cmdp = tolower(param_getchar(Cmd, 0)); + if ( cmdp == 'h') return usage_t55xx_dump(); + + bool usepwd = ( strlen(Cmd) > 0); + if ( usepwd ){ + password = param_get32ex(Cmd, 0, 0, 16); + if (param_getchar(Cmd, 1) =='o' ) + override = true; + } + + printT5xxHeader(0); + for ( uint8_t i = 0; i < 8; ++i) + T55xxReadBlock(i, 0, usepwd, override, password); + + printT5xxHeader(1); + for ( uint8_t i = 0; i < 4; i++) + T55xxReadBlock(i, 1, usepwd, override, password); + + return 1; +} + +bool AquireData( uint8_t page, uint8_t block, bool pwdmode, uint32_t password ) { + // arg0 bitmodes: + // bit0 = pwdmode + // bit1 = page to read from + // arg1: which block to read + // arg2: password + uint8_t arg0 = (page<<1) | pwdmode; + UsbCommand c = {CMD_T55XX_READ_BLOCK, {arg0, block, password}}; + clearCommandBuffer(); + SendCommand(&c); + if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2500) ) { + PrintAndLogEx(WARNING, "command execution time out"); + return false; + } + + uint8_t got[8000]; + if ( !GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 4000, true)) { + PrintAndLogEx(WARNING, "command execution time out"); + return false; + } + setGraphBuf(got, sizeof(got)); + // set signal properties low/high/mean/amplitude and is_noise detection + computeSignalProperties(got, sizeof(got)); + RepaintGraphWindow(); + return !getSignalProperties()->isnoise; +} + +char * GetBitRateStr(uint32_t id, bool xmode) { + static char buf[25]; + + char *retStr = buf; + if (xmode) { //xmode bitrate calc is same as em4x05 calc + snprintf(retStr,sizeof(buf),"%d - RF/%d", id, EM4x05_GET_BITRATE(id)); + } else { + switch (id){ + case 0: snprintf(retStr,sizeof(buf),"%d - RF/8",id); break; + case 1: snprintf(retStr,sizeof(buf),"%d - RF/16",id); break; + case 2: snprintf(retStr,sizeof(buf),"%d - RF/32",id); break; + case 3: snprintf(retStr,sizeof(buf),"%d - RF/40",id); break; + case 4: snprintf(retStr,sizeof(buf),"%d - RF/50",id); break; + case 5: snprintf(retStr,sizeof(buf),"%d - RF/64",id); break; + case 6: snprintf(retStr,sizeof(buf),"%d - RF/100",id); break; + case 7: snprintf(retStr,sizeof(buf),"%d - RF/128",id); break; + default: snprintf(retStr,sizeof(buf),"%d - (Unknown)",id); break; + } + } + return buf; +} + +char * GetSaferStr(uint32_t id){ + static char buf[40]; + char *retStr = buf; + + snprintf(retStr,sizeof(buf),"%d",id); + if (id == 6) { + snprintf(retStr,sizeof(buf),"%d - passwd",id); + } + if (id == 9 ){ + snprintf(retStr,sizeof(buf),"%d - testmode",id); + } + + return buf; +} + +char * GetModulationStr( uint32_t id){ + static char buf[60]; + char *retStr = buf; + + switch (id){ + case 0: snprintf(retStr,sizeof(buf),"%d - DIRECT (ASK/NRZ)",id); break; + case 1: snprintf(retStr,sizeof(buf),"%d - PSK 1 phase change when input changes",id); break; + case 2: snprintf(retStr,sizeof(buf),"%d - PSK 2 phase change on bitclk if input high",id); break; + case 3: snprintf(retStr,sizeof(buf),"%d - PSK 3 phase change on rising edge of input",id); break; + case 4: snprintf(retStr,sizeof(buf),"%d - FSK 1 RF/8 RF/5",id); break; + case 5: snprintf(retStr,sizeof(buf),"%d - FSK 2 RF/8 RF/10",id); break; + case 6: snprintf(retStr,sizeof(buf),"%d - FSK 1a RF/5 RF/8",id); break; + case 7: snprintf(retStr,sizeof(buf),"%d - FSK 2a RF/10 RF/8",id); break; + case 8: snprintf(retStr,sizeof(buf),"%d - Manchester",id); break; + case 16: snprintf(retStr,sizeof(buf),"%d - Biphase",id); break; + case 0x18:snprintf(retStr,sizeof(buf),"%d - Biphase a - AKA Conditional Dephase Encoding(CDP)",id); break; + case 17: snprintf(retStr,sizeof(buf),"%d - Reserved",id); break; + default: snprintf(retStr,sizeof(buf),"0x%02X (Unknown)",id); break; + } + return buf; +} + +char * GetModelStrFromCID(uint32_t cid){ + + static char buf[10]; + char *retStr = buf; + + if (cid == 1) snprintf(retStr, sizeof(buf),"ATA5577M1"); + if (cid == 2) snprintf(retStr, sizeof(buf),"ATA5577M2"); + return buf; +} + +char * GetSelectedModulationStr( uint8_t id){ + + static char buf[20]; + char *retStr = buf; + + switch (id){ + case DEMOD_FSK: snprintf(retStr,sizeof(buf),"FSK"); break; + case DEMOD_FSK1: snprintf(retStr,sizeof(buf),"FSK1"); break; + case DEMOD_FSK1a: snprintf(retStr,sizeof(buf),"FSK1a"); break; + case DEMOD_FSK2: snprintf(retStr,sizeof(buf),"FSK2"); break; + case DEMOD_FSK2a: snprintf(retStr,sizeof(buf),"FSK2a"); break; + case DEMOD_ASK: snprintf(retStr,sizeof(buf),"ASK"); break; + case DEMOD_NRZ: snprintf(retStr,sizeof(buf),"DIRECT/NRZ"); break; + case DEMOD_PSK1: snprintf(retStr,sizeof(buf),"PSK1"); break; + case DEMOD_PSK2: snprintf(retStr,sizeof(buf),"PSK2"); break; + case DEMOD_PSK3: snprintf(retStr,sizeof(buf),"PSK3"); break; + case DEMOD_BI: snprintf(retStr,sizeof(buf),"BIPHASE"); break; + case DEMOD_BIa: snprintf(retStr,sizeof(buf),"BIPHASEa - (CDP)"); break; + default: snprintf(retStr,sizeof(buf),"(Unknown)"); break; + } + return buf; +} + +void t55x7_create_config_block( int tagtype ){ + + /* + T55X7_DEFAULT_CONFIG_BLOCK, T55X7_RAW_CONFIG_BLOCK + T55X7_EM_UNIQUE_CONFIG_BLOCK, T55X7_FDXB_CONFIG_BLOCK, + T55X7_FDXB_CONFIG_BLOCK, T55X7_HID_26_CONFIG_BLOCK, T55X7_INDALA_64_CONFIG_BLOCK, T55X7_INDALA_224_CONFIG_BLOCK + T55X7_GUARDPROXII_CONFIG_BLOCK, T55X7_VIKING_CONFIG_BLOCK, T55X7_NORALYS_CONFIG_BLOCK, T55X7_IOPROX_CONFIG_BLOCK + */ + static char buf[60]; + char *retStr = buf; + + switch (tagtype){ + case 0: snprintf(retStr, sizeof(buf),"%08X - T55X7 Default", T55X7_DEFAULT_CONFIG_BLOCK); break; + case 1: snprintf(retStr, sizeof(buf),"%08X - T55X7 Raw", T55X7_RAW_CONFIG_BLOCK); break; + case 2: snprintf(retStr, sizeof(buf),"%08X - T5555 Q5 Default", T5555_DEFAULT_CONFIG_BLOCK); break; + default: + break; + } + PrintAndLogEx(NORMAL, buf); +} + +int CmdResetRead(const char *Cmd) { + UsbCommand c = {CMD_T55XX_RESET_READ, {0,0,0}}; + clearCommandBuffer(); + SendCommand(&c); + if ( !WaitForResponseTimeout(CMD_ACK, NULL, 2500) ) { + PrintAndLogEx(WARNING, "command execution time out"); + return 0; + } + + uint8_t got[BIGBUF_SIZE-1]; + if ( !GetFromDevice(BIG_BUF, got, sizeof(got), 0, NULL, 2500, false)) { + PrintAndLogEx(WARNING, "command execution time out"); + return 0; + } + setGraphBuf(got, sizeof(got)); + return 1; +} + +int CmdT55xxWipe(const char *Cmd) { + char writeData[20] = {0}; + char *ptrData = writeData; + char cmdp = param_getchar(Cmd, 0); + if ( cmdp == 'h' || cmdp == 'H') return usage_t55xx_wipe(); + + bool Q5 = (cmdp == 'q' || cmdp == 'Q'); + + // Try with the default password to reset block 0 + // With a pwd should work even if pwd bit not set + PrintAndLogEx(INFO, "\nBeginning Wipe of a T55xx tag (assuming the tag is not password protected)\n"); + + if ( Q5 ) + snprintf(ptrData,sizeof(writeData),"b 0 d 6001F004 p 0"); + else + snprintf(ptrData,sizeof(writeData),"b 0 d 000880E0 p 0"); + + if (!CmdT55xxWriteBlock(ptrData)) PrintAndLogEx(WARNING, "Error writing blk 0"); + + for (uint8_t blk = 1; blk<8; blk++) { + + snprintf(ptrData,sizeof(writeData),"b %d d 0", blk); + + if (!CmdT55xxWriteBlock(ptrData)) PrintAndLogEx(WARNING, "Error writing blk %d", blk); + + memset(writeData, 0x00, sizeof(writeData)); + } + return 0; +} + +bool IsCancelled(void) { + if (ukbhit()) { + int gc = getchar(); (void)gc; + PrintAndLogEx(NORMAL, "\naborted via keyboard!\n"); + return true; + } + return false; +} + +int CmdT55xxChkPwds(const char *Cmd) { + // load a default pwd file. + char line[9]; + char filename[FILE_PATH_SIZE] = {0}; + int keycnt = 0; + uint8_t stKeyBlock = 20; + uint8_t *keyBlock = NULL, *p = NULL; + bool found = false; + uint8_t timeout = 0; + + memset(line, 0, sizeof(line)); + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (strlen(Cmd) == 0 || cmdp == 'h') return usage_t55xx_chk(); + + /* + if ( T55xxReadBlock(7, 0, 0, 0, 0) ) { + + // now try to validate it.. + PrintAndLogEx(WARNING, "\n Block 7 was readable"); + return 1; + } + */ + + uint64_t t1 = msclock(); + + if ( cmdp == 'm' ) { + UsbCommand c = {CMD_T55XX_CHKPWDS, {0,0,0} }; + clearCommandBuffer(); + SendCommand(&c); + UsbCommand resp; + + while ( !WaitForResponseTimeout(CMD_ACK, &resp, 2000) ) { + timeout++; + printf("."); fflush(stdout); + if (timeout > 180) { + PrintAndLogEx(WARNING, "\nno response from Proxmark. Aborting..."); + return 2; + } + } + + if ( resp.arg[0] ) { + PrintAndLogEx(SUCCESS, "\nFound a candidate [ %08X ]. Trying to validate", resp.arg[1]); + + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, resp.arg[1])) { + PrintAndLogEx(INFO, "Aquireing data from device failed. Quitting"); + return 2; + } + + found = tryDetectModulation(); + if (found) { + PrintAndLogEx(SUCCESS, "Found valid password: [ %08X ]", resp.arg[1]); + } else { + PrintAndLogEx(WARNING, "Password NOT found."); + } + } else { + PrintAndLogEx(WARNING, "Password NOT found."); + } + + goto out; + } + + keyBlock = calloc(stKeyBlock, 4); + if (keyBlock == NULL) return 1; + + if (cmdp == 'i') { + + int len = strlen(Cmd+2); + if (len > FILE_PATH_SIZE) len = FILE_PATH_SIZE; + memcpy(filename, Cmd+2, len); + + FILE * f = fopen( filename , "r"); + if ( !f ) { + PrintAndLogEx(FAILED, "File: " _YELLOW_(%s) ": not found or locked.", filename); + free(keyBlock); + return 1; + } + + while( fgets(line, sizeof(line), f) ){ + if (strlen(line) < 8 || line[7] == '\n') continue; + + //goto next line + while (fgetc(f) != '\n' && !feof(f)) ; + + //The line start with # is comment, skip + if( line[0]=='#' ) continue; + + if (!isxdigit(line[0])) { + PrintAndLogEx(WARNING, "File content error. '%s' must include 8 HEX symbols", line); + continue; + } + + line[8] = 0; + + // realloc keyblock array size. + if ( stKeyBlock - keycnt < 2) { + p = realloc(keyBlock, 4 * (stKeyBlock += 10)); + if (!p) { + PrintAndLogEx(WARNING, "Cannot allocate memory for defaultKeys"); + free(keyBlock); + if (f) + fclose(f); + return 2; + } + keyBlock = p; + } + // clear mem + memset(keyBlock + 4 * keycnt, 0, 4); + + num_to_bytes( strtoll(line, NULL, 16), 4, keyBlock + 4*keycnt); + +// PrintAndLogEx(NORMAL, "chk custom pwd[%2d] %08X", keycnt, bytes_to_num(keyBlock + 4 * keycnt, 4) ); + keycnt++; + memset(line, 0, sizeof(line)); + } + + if (f) + fclose(f); + + if (keycnt == 0) { + PrintAndLogEx(WARNING, "No keys found in file"); + free(keyBlock); + return 1; + } + PrintAndLogEx(SUCCESS, "Loaded %d keys", keycnt); + + // loop + uint64_t testpwd = 0x00; + for (uint16_t c = 0; c < keycnt; ++c ) { + + if ( IsOffline() ) { + PrintAndLogEx(WARNING, "Device offline\n"); + free(keyBlock); + return 2; + } + + if (IsCancelled()) { + free(keyBlock); + return 0; + } + + testpwd = bytes_to_num(keyBlock + 4*c, 4); + + PrintAndLogEx(INFO, "Testing %08X", testpwd); + + if ( !AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, testpwd)) { + PrintAndLogEx(INFO, "Aquireing data from device failed. Quitting"); + free(keyBlock); + return 0; + } + + found = tryDetectModulation(); + if ( found ) + break; + + } + if ( found ) + PrintAndLogEx(SUCCESS, "Found valid password: [ %08X ]", testpwd); + else + PrintAndLogEx(WARNING, "Password NOT found."); + } + + free(keyBlock); + +out: + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\nTime in bruteforce: %.0f seconds\n", (float)t1/1000.0); + return 0; +} + +int CmdT55xxBruteForce(const char *Cmd) { + + uint32_t start_password = 0x00000000; //start password + uint32_t end_password = 0xFFFFFFFF; //end password + uint32_t curr = 0; + bool found = false; + + + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h') return usage_t55xx_bruteforce(); + + uint64_t t1 = msclock(); + + // Try to read Block 7, first :) + + // incremental pwd range search + start_password = param_get32ex(Cmd, 0, 0, 16); + end_password = param_get32ex(Cmd, 1, 0, 16); + + curr = start_password; + + if ( start_password >= end_password ) { + return usage_t55xx_bruteforce(); + } + + PrintAndLogEx(INFO, "Search password range [%08X -> %08X]", start_password, end_password); + + while ( !found ){ + + printf("."); fflush(stdout); + + if (IsCancelled()) { + return 0; + } + + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, curr)) { + PrintAndLogEx(WARNING, "Aquiring data from device failed. Quitting"); + return 0; + } + + found = tryDetectModulation(); + + if (curr == end_password) + break; + curr++; + } + + PrintAndLogEx(NORMAL, ""); + + if (found) + PrintAndLogEx(SUCCESS, "Found valid password: [ %08X ]", curr); + else + PrintAndLogEx(WARNING, "Password NOT found. Last tried: [ %08X ]", --curr); + + t1 = msclock() - t1; + PrintAndLogEx(SUCCESS, "\nTime in bruteforce: %.0f seconds\n", (float)t1/1000.0); + return 0; +} + +int tryOnePassword(uint32_t password) { + PrintAndLogEx(INFO, "Trying password %08x", password); + if (!AquireData(T55x7_PAGE0, T55x7_CONFIGURATION_BLOCK, true, password)) { + PrintAndLogEx(NORMAL, "Acquire data from device failed. Quitting"); + return -1; + } + + if (tryDetectModulation()) + return 1; + else + return 0; +} + +int CmdT55xxRecoverPW(const char *Cmd) { + int bit = 0; + uint32_t orig_password = 0x0; + uint32_t curr_password = 0x0; + uint32_t prev_password = 0xffffffff; + uint32_t mask = 0x0; + int found = 0; + char cmdp = tolower(param_getchar(Cmd, 0)); + if (cmdp == 'h' ) return usage_t55xx_recoverpw(); + + orig_password = param_get32ex(Cmd, 0, 0x51243648, 16); //password used by handheld cloners + + // first try fliping each bit in the expected password + while (bit < 32) { + curr_password = orig_password ^ ( 1 << bit ); + found = tryOnePassword(curr_password); + if (found == -1) return 0; + bit++; + + if (IsCancelled()) return 0; + } + + // now try to use partial original password, since block 7 should have been completely + // erased during the write sequence and it is possible that only partial password has been + // written + // not sure from which end the bit bits are written, so try from both ends + // from low bit to high bit + bit = 0; + while (bit < 32) { + mask += ( 1 << bit ); + curr_password = orig_password & mask; + // if updated mask didn't change the password, don't try it again + if (prev_password == curr_password) { + bit++; + continue; + } + found = tryOnePassword(curr_password); + if (found == -1) return 0; + bit++; + prev_password = curr_password; + + if (IsCancelled()) return 0; + } + + // from high bit to low + bit = 0; + mask = 0xffffffff; + while (bit < 32) { + mask -= ( 1 << bit ); + curr_password = orig_password & mask; + // if updated mask didn't change the password, don't try it again + if (prev_password == curr_password) { + bit++; + continue; + } + found = tryOnePassword(curr_password); + if (found == -1) + return 0; + bit++; + prev_password = curr_password; + + if (IsCancelled()) return 0; + } + + PrintAndLogEx(NORMAL, ""); + + if (found == 1) + PrintAndLogEx(SUCCESS, "Found valid password: [%08x]", curr_password); + else + PrintAndLogEx(WARNING, "Password NOT found."); + + return 0; +} + +// note length of data returned is different for different chips. +// some return all page 1 (64 bits) and others return just that block (32 bits) +// unfortunately the 64 bits makes this more likely to get a false positive... +bool tryDetectP1(bool getData) { + uint8_t preamble[] = {1,1,1,0,0,0,0,0,0,0,0,1,0,1,0,1}; + size_t startIdx = 0; + uint8_t fc1 = 0, fc2 = 0, ans = 0; + int clk = 0, firstClockEdge = 0; + bool st = true; + + if ( getData ) { + if ( !AquireData(T55x7_PAGE1, 1, false, 0) ) + return false; + } + + // try fsk clock detect. if successful it cannot be any other type of modulation... (in theory...) + ans = fskClocks(&fc1, &fc2, (uint8_t *)&clk, &firstClockEdge); + if (ans && ((fc1==10 && fc2==8) || (fc1==8 && fc2==5))) { + if ( FSKrawDemod("0 0", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + if ( FSKrawDemod("0 1", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + return false; + } + + // try ask clock detect. it could be another type even if successful. + clk = GetAskClock("", false); + if (clk > 0) { + if ( ASKDemod_ext("0 0 1", false, false, 1, &st) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + st = true; + if ( ASKDemod_ext("0 1 1", false, false, 1, &st) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + if ( ASKbiphaseDemod("0 0 0 2", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + if ( ASKbiphaseDemod("0 0 1 2", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + } + + // try NRZ clock detect. it could be another type even if successful. + clk = GetNrzClock("", false); //has the most false positives :( + if (clk > 0) { + if ( NRZrawDemod("0 0 1", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + if ( NRZrawDemod("0 1 1", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + return true; + } + } + + // Fewer card uses PSK + // try psk clock detect. if successful it cannot be any other type of modulation... (in theory...) + clk = GetPskClock("", false); + if (clk > 0) { + // allow undo + // save_restoreGB(1); + // skip first 160 samples to allow antenna to settle in (psk gets inverted occasionally otherwise) + //CmdLtrim("160"); + if ( PSKDemod("0 0 6", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + //save_restoreGB(0); + return true; + } + if ( PSKDemod("0 1 6", false) && + preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + //save_restoreGB(0); + return true; + } + // PSK2 - needs a call to psk1TOpsk2. + if ( PSKDemod("0 0 6", false)) { + psk1TOpsk2(DemodBuffer, DemodBufferLen); + if (preambleSearchEx(DemodBuffer, preamble, sizeof(preamble), &DemodBufferLen, &startIdx, false) && + (DemodBufferLen == 32 || DemodBufferLen == 64) ) { + //save_restoreGB(0); + return true; + } + } // inverse waves does not affect PSK2 demod + //undo trim samples + //save_restoreGB(0); + // no other modulation clocks = 2 or 4 so quit searching + if (fc1 != 8) return false; + } + + return false; +} +// does this need to be a callable command? +int CmdT55xxDetectPage1(const char *Cmd){ + bool errors = false; + bool useGB = false; + bool usepwd = false; + uint32_t password = 0; + uint8_t cmdp = 0; + + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_t55xx_detectP1(); + case 'p': + password = param_get32ex(Cmd, cmdp+1, 0, 16); + usepwd = true; + cmdp += 2; + break; + case '1': + // use Graphbuffer data + useGB = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = true; + break; + } + } + if (errors) return usage_t55xx_detectP1(); + + if ( !useGB ) { + if ( !AquireData(T55x7_PAGE1, 1, usepwd, password) ) + return false; + } + bool success = tryDetectP1(false); + if (success) PrintAndLogEx(SUCCESS, "T55xx chip found!"); + return success; +} + +int CmdT55xxSetDeviceConfig(const char *Cmd){ + uint8_t startgap = 0, writegap = 0; + uint8_t write0 = 0, write1 = 0, readgap = 0; + bool errors = false, shall_persist = false; + uint8_t cmdp = 0; + while (param_getchar(Cmd, cmdp) != 0x00 && !errors) { + switch (tolower(param_getchar(Cmd, cmdp))) { + case 'h': + return usage_lf_deviceconfig(); + case 'a': + errors |= param_getdec(Cmd, cmdp+1, &startgap); + cmdp += 2; + break; + case 'b': + errors |= param_getdec(Cmd, cmdp+1, &writegap); + cmdp += 2; + break; + case 'c': + errors |= param_getdec(Cmd, cmdp+1, &write0); + cmdp += 2; + break; + case 'd': + errors |= param_getdec(Cmd, cmdp+1, &write1); + cmdp += 2; + break; + case 'e': + errors |= param_getdec(Cmd, cmdp+1, &readgap); + cmdp += 2; + break; + case 'p': + shall_persist = true; + cmdp++; + break; + default: + PrintAndLogEx(WARNING, "Unknown parameter '%c'", param_getchar(Cmd, cmdp)); + errors = 1; + break; + } + } + + //Validations + if (errors || cmdp == 0) return usage_lf_deviceconfig(); + + t55xx_config config = { startgap*8, writegap*8, write0*8, write1*8, readgap*8 }; + + UsbCommand c = {CMD_SET_LF_T55XX_CONFIG, {shall_persist,0,0} }; + memcpy(c.d.asBytes, &config, sizeof(t55xx_config)); + clearCommandBuffer(); + SendCommand(&c); + return 0; +} + +static command_t CommandTable[] = { + {"help", CmdHelp, 1, "This help"}, + {"bruteforce", CmdT55xxBruteForce,0, " [i <*.dic>] Simple bruteforce attack to find password"}, + {"config", CmdT55xxSetConfig, 1, "Set/Get T55XX configuration (modulation, inverted, offset, rate)"}, + {"chk", CmdT55xxChkPwds, 1, "Check passwords"}, + {"detect", CmdT55xxDetect, 1, "[1] Try detecting the tag modulation from reading the configuration block."}, + {"deviceconfig", CmdT55xxSetDeviceConfig, 1, "Set/Get T55XX device configuration (startgap, writegap, write0, write1, readgap"}, + {"p1detect", CmdT55xxDetectPage1,1, "[1] Try detecting if this is a t55xx tag by reading page 1"}, + {"dump", CmdT55xxDump, 0, "[password] [o] Dump T55xx card block 0-7. Optional [password], [override]"}, + {"info", CmdT55xxInfo, 1, "[1] Show T55x7 configuration data (page 0/ blk 0)"}, + {"read", CmdT55xxReadBlock, 0, "b p [password] [o] [1] -- Read T55xx block data. Optional [p password], [override], [page1]"}, + {"resetread", CmdResetRead, 0, "Send Reset Cmd then lf read the stream to attempt to identify the start of it (needs a demod and/or plot after)"}, + {"recoverpw", CmdT55xxRecoverPW, 0, "[password] Try to recover from bad password write from a cloner. Only use on PW protected chips!"}, + {"special", special, 0, "Show block changes with 64 different offsets"}, + {"trace", CmdT55xxReadTrace, 1, "[1] Show T55x7 traceability data (page 1/ blk 0-1)"}, + {"wakeup", CmdT55xxWakeUp, 0, "Send AOR wakeup command"}, + {"wipe", CmdT55xxWipe, 0, "[q] Wipe a T55xx tag and set defaults (will destroy any data on tag)"}, + {"write", CmdT55xxWriteBlock,0, "b d p [password] [1] -- Write T55xx block data. Optional [p password], [page1]"}, + {NULL, NULL, 0, NULL} +}; + +int CmdLFT55XX(const char *Cmd) { + clearCommandBuffer(); + CmdsParse(CommandTable, Cmd); + return 0; +} + +int CmdHelp(const char *Cmd) { + CmdsHelp(CommandTable); + return 0; +} diff --git a/client/cmdlft55xx.h b/client/cmdlft55xx.h index 46de0c359..775f7ccca 100644 --- a/client/cmdlft55xx.h +++ b/client/cmdlft55xx.h @@ -1,171 +1,171 @@ -//----------------------------------------------------------------------------- -// -// 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. -//----------------------------------------------------------------------------- -// Low frequency T55xx commands -//----------------------------------------------------------------------------- - -#ifndef CMDLFT55XX_H__ -#define CMDLFT55XX_H__ - -#include -#include -#include -#include -#include -#include "proxmark3.h" -#include "ui.h" -#include "graph.h" -#include "comms.h" -#include "cmdparser.h" -#include "cmddata.h" -#include "cmdlf.h" -#include "util.h" -#include "lfdemod.h" -#include "cmdhf14a.h" //for getTagInfo - - -#define T55x7_CONFIGURATION_BLOCK 0x00 -#define T55x7_PAGE0 0x00 -#define T55x7_PAGE1 0x01 -#define T55x7_PWD 0x00000010 -#define REGULAR_READ_MODE_BLOCK 0xFF - -// config blocks -#define T55X7_DEFAULT_CONFIG_BLOCK 0x000880E8 // ASK, compat mode, data rate 32, manchester, STT, 7 data blocks -#define T55X7_RAW_CONFIG_BLOCK 0x000880E0 // ASK, compat mode, data rate 32, manchester, 7 data blocks -#define T55X7_EM_UNIQUE_CONFIG_BLOCK 0x00148040 // ASK, emulate em4x02/unique - compat mode, manchester, data rate 64, 2 data blocks -// FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag -// we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo, -// and it should actually be "10000" (16) -// #define T55X7_FDXB_CONFIG_BLOCK 903F8080 // emulate fdx-b - xtended mode, BiPhase ('57), data rate 32, 4 data blocks -#define T55X7_FDXB_CONFIG_BLOCK 0x903F0082 // emulate fdx-b - xtended mode, BiPhase ('50), invert data, data rate 32, 4 data blocks -#define T55X7_HID_26_CONFIG_BLOCK 0x00107060 // hid 26 bit - compat mode, FSK2a, data rate 50, 3 data blocks -#define T55X7_PYRAMID_CONFIG_BLOCK 0x00107080 // Pyramid 26 bit - compat mode, FSK2a, data rate 50, 4 data blocks -#define T55X7_INDALA_64_CONFIG_BLOCK 0x00081040 // emulate indala 64 bit - compat mode, PSK1, psk carrier FC * 2, data rate 32, maxblock 2 -#define T55X7_INDALA_224_CONFIG_BLOCK 0x000810E0 // emulate indala 224 bit - compat mode, PSK1, psk carrier FC * 2, data rate 32, maxblock 7 -#define T55X7_GUARDPROXII_CONFIG_BLOCK 0x00150060 // bitrate 64pcb, Direct modulation, Biphase, 3 data blocks -#define T55X7_VIKING_CONFIG_BLOCK 0x00088040 // ASK, compat mode, data rate 32, Manchester, 2 data blocks -#define T55X7_NORALYS_CONFIG_BLOCK 0x00088C6A // ASK, compat mode, (NORALYS - KCP3000) -#define T55X7_IOPROX_CONFIG_BLOCK 0x00147040 // ioprox - FSK2a, data rate 64, 2 data blocks -#define T55X7_PRESCO_CONFIG_BLOCK 0x00088088 // ASK, data rate 32, Manchester, 5 data blocks, STT -#define T55X7_NEDAP_64_CONFIG_BLOCK 0x907f0042 // BiPhase, data rate 64, 3 data blocks -#define T55X7_NEDAP_128_CONFIG_BLOCK 0x907f0082 // BiPhase, data rate 64, 5 data blocks -#define T55X7_bin 0b0010 - -#define T5555_DEFAULT_CONFIG_BLOCK 0x6001F004 // data rate 64 , ask, manchester, 2 data blocks? -enum { - T55x7_RAW = 0x00, - T55x7_DEFAULT = 0x00, - T5555_DEFAULT = 0x01, - EM_UNIQUE = 0x0, - FDBX = 0x02, - HID_26 = 0x03, - INDALA_64 = 0x04, - INDALA_224 = 0x05, - GUARDPROXXII = 0x06, - VIKING = 0x07, - NORALSYS = 0x08, - IOPROX = 0x09, - NEDAP_64 = 0x0A, - NEDAP_128 = 0x0B, -} t55xx_tag; - -typedef struct { - uint32_t bl1; - uint32_t bl2; - uint32_t acl; - uint32_t mfc; - uint32_t cid; - uint32_t year; - uint32_t quarter; - uint32_t icr; - uint32_t lotid; - uint32_t wafer; - uint32_t dw; -} t55x7_tracedata_t; - -typedef struct { - uint32_t bl1; - uint32_t bl2; - uint32_t icr; - char lotidc; - uint32_t lotid; - uint32_t wafer; - uint32_t dw; -} t5555_tracedata_t; - -typedef struct { - enum { - DEMOD_NRZ = 0x00, - DEMOD_PSK1 = 0x01, - DEMOD_PSK2 = 0x02, - DEMOD_PSK3 = 0x03, - DEMOD_FSK1 = 0x04, - DEMOD_FSK1a = 0x05, - DEMOD_FSK2 = 0x06, - DEMOD_FSK2a = 0x07, - DEMOD_FSK = 0xF0, //generic FSK (auto detect FCs) - DEMOD_ASK = 0x08, - DEMOD_BI = 0x10, - DEMOD_BIa = 0x18, - } modulation; - bool inverted; - uint8_t offset; - uint32_t block0; - enum { - RF_8 = 0x00, - RF_16 = 0x01, - RF_32 = 0x02, - RF_40 = 0x03, - RF_50 = 0x04, - RF_64 = 0x05, - RF_100 = 0x06, - RF_128 = 0x07, - } bitrate; - bool Q5; - bool ST; -} t55xx_conf_block_t; - -t55xx_conf_block_t Get_t55xx_Config(void); -void Set_t55xx_Config(t55xx_conf_block_t conf); - -extern int CmdLFT55XX(const char *Cmd); -extern int CmdT55xxChk(const char *Cmd); -extern int CmdT55xxBruteForce(const char *Cmd); -extern int CmdT55xxSetConfig(const char *Cmd); -extern int CmdT55xxReadBlock(const char *Cmd); -extern int CmdT55xxWriteBlock(const char *Cmd); -extern int CmdT55xxReadTrace(const char *Cmd); -extern int CmdT55xxInfo(const char *Cmd); -extern int CmdT55xxDetect(const char *Cmd); -extern int CmdResetRead(const char *Cmd); -extern int CmdT55xxWipe(const char *Cmd); - -char * GetBitRateStr(uint32_t id, bool xmode); -char * GetSaferStr(uint32_t id); -char * GetModulationStr( uint32_t id); -char * GetModelStrFromCID(uint32_t cid); -char * GetSelectedModulationStr( uint8_t id); -uint32_t PackBits(uint8_t start, uint8_t len, uint8_t *bitstream); -void printT5xxHeader(uint8_t page); -void printT55xxBlock(const char *demodStr); -int printConfiguration( t55xx_conf_block_t b); - -bool DecodeT55xxBlock(void); -bool tryDetectModulation(void); -bool testKnownConfigBlock(uint32_t block0); -extern bool tryDetectP1(bool getData); -bool test(uint8_t mode, uint8_t *offset, int *fndBitRate, uint8_t clk, bool *Q5); -int special(const char *Cmd); -bool AquireData( uint8_t page, uint8_t block, bool pwdmode, uint32_t password ); -bool AquireDataEx( uint8_t page, uint8_t block, bool pwdmode, uint32_t password, uint32_t timing ) ; - -bool detectPassword(int password); - -void printT55x7Trace( t55x7_tracedata_t data, uint8_t repeat ); -void printT5555Trace( t5555_tracedata_t data, uint8_t repeat ); - -#endif +//----------------------------------------------------------------------------- +// +// 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. +//----------------------------------------------------------------------------- +// Low frequency T55xx commands +//----------------------------------------------------------------------------- + +#ifndef CMDLFT55XX_H__ +#define CMDLFT55XX_H__ + +#include +#include +#include +#include +#include +#include "proxmark3.h" +#include "ui.h" +#include "graph.h" +#include "comms.h" +#include "cmdparser.h" +#include "cmddata.h" +#include "cmdlf.h" +#include "util.h" +#include "lfdemod.h" +#include "cmdhf14a.h" //for getTagInfo + + +#define T55x7_CONFIGURATION_BLOCK 0x00 +#define T55x7_PAGE0 0x00 +#define T55x7_PAGE1 0x01 +#define T55x7_PWD 0x00000010 +#define REGULAR_READ_MODE_BLOCK 0xFF + +// config blocks +#define T55X7_DEFAULT_CONFIG_BLOCK 0x000880E8 // ASK, compat mode, data rate 32, manchester, STT, 7 data blocks +#define T55X7_RAW_CONFIG_BLOCK 0x000880E0 // ASK, compat mode, data rate 32, manchester, 7 data blocks +#define T55X7_EM_UNIQUE_CONFIG_BLOCK 0x00148040 // ASK, emulate em4x02/unique - compat mode, manchester, data rate 64, 2 data blocks +// FDXB requires data inversion and BiPhase 57 is simply BiPhase 50 inverted, so we can either do it using the modulation scheme or the inversion flag +// we've done both below to prove that it works either way, and the modulation value for BiPhase 50 in the Atmel data sheet of binary "10001" (17) is a typo, +// and it should actually be "10000" (16) +// #define T55X7_FDXB_CONFIG_BLOCK 903F8080 // emulate fdx-b - xtended mode, BiPhase ('57), data rate 32, 4 data blocks +#define T55X7_FDXB_CONFIG_BLOCK 0x903F0082 // emulate fdx-b - xtended mode, BiPhase ('50), invert data, data rate 32, 4 data blocks +#define T55X7_HID_26_CONFIG_BLOCK 0x00107060 // hid 26 bit - compat mode, FSK2a, data rate 50, 3 data blocks +#define T55X7_PYRAMID_CONFIG_BLOCK 0x00107080 // Pyramid 26 bit - compat mode, FSK2a, data rate 50, 4 data blocks +#define T55X7_INDALA_64_CONFIG_BLOCK 0x00081040 // emulate indala 64 bit - compat mode, PSK1, psk carrier FC * 2, data rate 32, maxblock 2 +#define T55X7_INDALA_224_CONFIG_BLOCK 0x000810E0 // emulate indala 224 bit - compat mode, PSK1, psk carrier FC * 2, data rate 32, maxblock 7 +#define T55X7_GUARDPROXII_CONFIG_BLOCK 0x00150060 // bitrate 64pcb, Direct modulation, Biphase, 3 data blocks +#define T55X7_VIKING_CONFIG_BLOCK 0x00088040 // ASK, compat mode, data rate 32, Manchester, 2 data blocks +#define T55X7_NORALYS_CONFIG_BLOCK 0x00088C6A // ASK, compat mode, (NORALYS - KCP3000) +#define T55X7_IOPROX_CONFIG_BLOCK 0x00147040 // ioprox - FSK2a, data rate 64, 2 data blocks +#define T55X7_PRESCO_CONFIG_BLOCK 0x00088088 // ASK, data rate 32, Manchester, 5 data blocks, STT +#define T55X7_NEDAP_64_CONFIG_BLOCK 0x907f0042 // BiPhase, data rate 64, 3 data blocks +#define T55X7_NEDAP_128_CONFIG_BLOCK 0x907f0082 // BiPhase, data rate 64, 5 data blocks +#define T55X7_bin 0b0010 + +#define T5555_DEFAULT_CONFIG_BLOCK 0x6001F004 // data rate 64 , ask, manchester, 2 data blocks? +enum { + T55x7_RAW = 0x00, + T55x7_DEFAULT = 0x00, + T5555_DEFAULT = 0x01, + EM_UNIQUE = 0x0, + FDBX = 0x02, + HID_26 = 0x03, + INDALA_64 = 0x04, + INDALA_224 = 0x05, + GUARDPROXXII = 0x06, + VIKING = 0x07, + NORALSYS = 0x08, + IOPROX = 0x09, + NEDAP_64 = 0x0A, + NEDAP_128 = 0x0B, +} t55xx_tag; + +typedef struct { + uint32_t bl1; + uint32_t bl2; + uint32_t acl; + uint32_t mfc; + uint32_t cid; + uint32_t year; + uint32_t quarter; + uint32_t icr; + uint32_t lotid; + uint32_t wafer; + uint32_t dw; +} t55x7_tracedata_t; + +typedef struct { + uint32_t bl1; + uint32_t bl2; + uint32_t icr; + char lotidc; + uint32_t lotid; + uint32_t wafer; + uint32_t dw; +} t5555_tracedata_t; + +typedef struct { + enum { + DEMOD_NRZ = 0x00, + DEMOD_PSK1 = 0x01, + DEMOD_PSK2 = 0x02, + DEMOD_PSK3 = 0x03, + DEMOD_FSK1 = 0x04, + DEMOD_FSK1a = 0x05, + DEMOD_FSK2 = 0x06, + DEMOD_FSK2a = 0x07, + DEMOD_FSK = 0xF0, //generic FSK (auto detect FCs) + DEMOD_ASK = 0x08, + DEMOD_BI = 0x10, + DEMOD_BIa = 0x18, + } modulation; + bool inverted; + uint8_t offset; + uint32_t block0; + enum { + RF_8 = 0x00, + RF_16 = 0x01, + RF_32 = 0x02, + RF_40 = 0x03, + RF_50 = 0x04, + RF_64 = 0x05, + RF_100 = 0x06, + RF_128 = 0x07, + } bitrate; + bool Q5; + bool ST; +} t55xx_conf_block_t; + +t55xx_conf_block_t Get_t55xx_Config(void); +void Set_t55xx_Config(t55xx_conf_block_t conf); + +extern int CmdLFT55XX(const char *Cmd); +extern int CmdT55xxChk(const char *Cmd); +extern int CmdT55xxBruteForce(const char *Cmd); +extern int CmdT55xxSetConfig(const char *Cmd); +extern int CmdT55xxReadBlock(const char *Cmd); +extern int CmdT55xxWriteBlock(const char *Cmd); +extern int CmdT55xxReadTrace(const char *Cmd); +extern int CmdT55xxInfo(const char *Cmd); +extern int CmdT55xxDetect(const char *Cmd); +extern int CmdResetRead(const char *Cmd); +extern int CmdT55xxWipe(const char *Cmd); + +char * GetBitRateStr(uint32_t id, bool xmode); +char * GetSaferStr(uint32_t id); +char * GetModulationStr( uint32_t id); +char * GetModelStrFromCID(uint32_t cid); +char * GetSelectedModulationStr( uint8_t id); +uint32_t PackBits(uint8_t start, uint8_t len, uint8_t *bitstream); +void printT5xxHeader(uint8_t page); +void printT55xxBlock(const char *demodStr); +int printConfiguration( t55xx_conf_block_t b); + +bool DecodeT55xxBlock(void); +bool tryDetectModulation(void); +bool testKnownConfigBlock(uint32_t block0); +extern bool tryDetectP1(bool getData); +bool test(uint8_t mode, uint8_t *offset, int *fndBitRate, uint8_t clk, bool *Q5); +int special(const char *Cmd); +bool AquireData( uint8_t page, uint8_t block, bool pwdmode, uint32_t password ); +bool AquireDataEx( uint8_t page, uint8_t block, bool pwdmode, uint32_t password, uint32_t timing ) ; + +bool detectPassword(int password); + +void printT55x7Trace( t55x7_tracedata_t data, uint8_t repeat ); +void printT5555Trace( t5555_tracedata_t data, uint8_t repeat ); + +#endif diff --git a/common/cmd.c b/common/cmd.c index dec095750..bed968c2c 100644 --- a/common/cmd.c +++ b/common/cmd.c @@ -1,64 +1,64 @@ -/* - * Proxmark send and receive commands - * - * Copyright (c) 2012, Roel Verdult - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * @file cmd.c - * @brief - */ -#include "cmd.h" - -uint8_t cmd_send(uint64_t cmd, uint64_t arg0, uint64_t arg1, uint64_t arg2, void* data, size_t len) { - UsbCommand txcmd; - - for (size_t i=0; i < sizeof(UsbCommand); i++) - ((uint8_t*)&txcmd)[i] = 0x00; - - // Compose the outgoing command frame - txcmd.cmd = cmd; - txcmd.arg[0] = arg0; - txcmd.arg[1] = arg1; - txcmd.arg[2] = arg2; - - // Add the (optional) content to the frame, with a maximum size of USB_CMD_DATA_SIZE - if (data && len) { - len = MIN(len, USB_CMD_DATA_SIZE); - for (size_t i=0; iUDP_CSR[(endpoint)]; \ - reg |= REG_NO_EFFECT_1_ALL; \ - reg &= ~(flags); \ - pUdp->UDP_CSR[(endpoint)] = reg; \ -} \ - -// reset flags in the UDP_CSR register and waits for synchronization -#define UDP_SET_EP_FLAGS(endpoint, flags) { \ - volatile unsigned int reg; \ - reg = pUdp->UDP_CSR[(endpoint)]; \ - reg |= REG_NO_EFFECT_1_ALL; \ - reg |= (flags); \ - pUdp->UDP_CSR[(endpoint)] = reg; \ -} \ - - -typedef struct { - uint32_t BitRate; - uint8_t Format; - uint8_t ParityType; - uint8_t DataBits; -} AT91S_CDC_LINE_CODING, *AT91PS_CDC_LINE_CODING; - -AT91S_CDC_LINE_CODING line = { - 115200, // baudrate - 0, // 1 Stop Bit - 0, // None Parity - 8}; // 8 Data bits - -static void SpinDelay(int ms) { - int us = ms * 1000; - int ticks = (48 * us) >> 10; - - // Borrow a PWM unit for my real-time clock - AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); - - // 48 MHz / 1024 gives 46.875 kHz - AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); - AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; - AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; - - uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - - for(;;) { - uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; - if (now == (uint16_t)(start + ticks)) - return; - - WDT_HIT(); - } -} - -//*---------------------------------------------------------------------------- -//* \fn usb_disable -//* \brief This function deactivates the USB device -//*---------------------------------------------------------------------------- -void usb_disable() { - // Disconnect the USB device - AT91C_BASE_PIOA->PIO_ODR = GPIO_USB_PU; - - // Clear all lingering interrupts - if (pUdp->UDP_ISR & AT91C_UDP_ENDBUSRES) { - pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; - } -} - -//*---------------------------------------------------------------------------- -//* \fn usb_enable -//* \brief This function Activates the USB device -//*---------------------------------------------------------------------------- -void usb_enable() { - // Set the PLL USB Divider - AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; - - // Specific Chip USB Initialisation - // Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock - AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_UDP; - AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); - - AT91C_BASE_UDP->UDP_FADDR = 0; - AT91C_BASE_UDP->UDP_GLBSTATE = 0; - - // Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO - // Set in PIO mode and Configure in Output - AT91C_BASE_PIOA->PIO_PER = GPIO_USB_PU; // Set in PIO mode - AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; // Configure as Output - - // Clear for set the Pullup resistor - AT91C_BASE_PIOA->PIO_CODR = GPIO_USB_PU; - - // Disconnect and reconnect USB controller for 100ms - usb_disable(); - - SpinDelay(100); - // Wait for a short while - //for (volatile size_t i=0; i<0x100000; i++) {}; - - // Reconnect USB reconnect - AT91C_BASE_PIOA->PIO_SODR = GPIO_USB_PU; - AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; -} - -//*---------------------------------------------------------------------------- -//* \fn usb_check -//* \brief Test if the device is configured and handle enumeration -//*---------------------------------------------------------------------------- -static int usb_reconnect = 0; -static int usb_configured = 0; -void SetUSBreconnect(int value) { - usb_reconnect = value; -} -int GetUSBreconnect(void) { - return usb_reconnect; -} -void SetUSBconfigured(int value) { - usb_configured = value; -} -int GetUSBconfigured(void){ - return usb_configured; -} - -bool usb_check() { - - /* - // reconnected ONCE and - if ( !USB_ATTACHED() ){ - usb_reconnect = 1; - return false; - } - - // only one time after USB been disengaged and re-engaged - if ( USB_ATTACHED() && usb_reconnect == 1 ) { - - if ( usb_configured == 0) { - usb_disable(); - usb_enable(); - - AT91F_CDC_Enumerate(); - - usb_configured = 1; - return false; - } - } - */ - - // interrupt status register - AT91_REG isr = pUdp->UDP_ISR; - - // end of bus reset - if (isr & AT91C_UDP_ENDBUSRES) { - pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; - // reset all endpoints - pUdp->UDP_RSTEP = (unsigned int)-1; - pUdp->UDP_RSTEP = 0; - // Enable the function - pUdp->UDP_FADDR = AT91C_UDP_FEN; - // Configure endpoint 0 (enable control endpoint) - pUdp->UDP_CSR[AT91C_EP_CONTROL] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); - } - else if (isr & AT91C_UDP_EPINT0) { - pUdp->UDP_ICR = AT91C_UDP_EPINT0; - AT91F_CDC_Enumerate(); - } - /* - else if (isr & AT91C_UDP_EPINT3 ) { - pUdp->UDP_ICR = AT91C_UDP_EPINT3; - AT91F_CDC_Enumerate(); - //pUdp->UDP_ICR |= AT91C_UDP_EPINT3; - } - */ - return (btConfiguration) ? true : false; -} - -bool usb_poll() { - if (!usb_check()) return false; - return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); -} - -/** - In github PR #129, some users appears to get a false positive from - usb_poll, which returns true, but the usb_read operation - still returns 0. - This check is basically the same as above, but also checks - that the length available to read is non-zero, thus hopefully fixes the - bug. -**/ -bool usb_poll_validate_length() { - if (!usb_check()) return false; - if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) return false; - return ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0; -} - -//*---------------------------------------------------------------------------- -//* \fn usb_read -//* \brief Read available data from Endpoint 1 OUT (host to device) -//*---------------------------------------------------------------------------- -uint32_t usb_read(byte_t* data, size_t len) { - - if ( len == 0 ) return 0; - - uint8_t bank = btReceiveBank; - uint32_t packetSize, nbBytesRcv = 0; - uint32_t time_out = 0; - - while (len) { - if (!usb_check()) break; - - if ( pUdp->UDP_CSR[AT91C_EP_OUT] & bank ) { - - packetSize = (pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16; - packetSize = MIN( packetSize, len); - len -= packetSize; - while (packetSize--) - data[nbBytesRcv++] = pUdp->UDP_FDR[AT91C_EP_OUT]; - - // flip bank - UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank) - - if (bank == AT91C_UDP_RX_DATA_BK0) - bank = AT91C_UDP_RX_DATA_BK1; - else - bank = AT91C_UDP_RX_DATA_BK0; - } - if (time_out++ == 0x1fff) break; - } - - btReceiveBank = bank; - return nbBytesRcv; -} - -//*---------------------------------------------------------------------------- -//* \fn usb_write -//* \brief Send through endpoint 2 (device to host) -//*---------------------------------------------------------------------------- -uint32_t usb_write(const byte_t* data, const size_t len) { - - if (!len) return 0; - if (!usb_check()) return 0; - - // can we write? - if ( (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) != 0 ) return 0; - - size_t length = len; - uint32_t cpt = 0; - - - // send first chunk - cpt = MIN(length, AT91C_EP_IN_SIZE); - length -= cpt; - while (cpt--) { - pUdp->UDP_FDR[AT91C_EP_IN] = *data++; - } - - UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); - - while (length) { - // Send next chunk - cpt = MIN(length, AT91C_EP_IN_SIZE); - length -= cpt; - while (cpt--) { - pUdp->UDP_FDR[AT91C_EP_IN] = *data++; - } - - // Wait for previous chunk to be sent - // (iceman) when is the bankswapping done? - while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { - if (!usb_check()) return length; - } - - UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); - UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); - - } - - // Wait for the end of transfer - while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { - if (!usb_check()) return length; - } - - UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); - - return length; -} - -//*---------------------------------------------------------------------------- -//* \fn AT91F_USB_SendData -//* \brief Send Data through the control endpoint -//*---------------------------------------------------------------------------- -void AT91F_USB_SendData(AT91PS_UDP pUdp, const char *pData, uint32_t length) { - uint32_t cpt = 0; - AT91_REG csr; - - do { - cpt = MIN(length, AT91C_EP_CONTROL_SIZE); - length -= cpt; - - while (cpt--) - pUdp->UDP_FDR[AT91C_EP_CONTROL] = *pData++; - - if (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) { - UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); - } - - UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXPKTRDY); - - do { - csr = pUdp->UDP_CSR[AT91C_EP_CONTROL]; - // Data IN stage has been stopped by a status OUT - if ( csr & AT91C_UDP_RX_DATA_BK0) { - - UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_RX_DATA_BK0) - return; - } - } while ( !(csr & AT91C_UDP_TXCOMP) ); - - } while (length); - - if (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) { - UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); - } -} - - -//*---------------------------------------------------------------------------- -//* \fn AT91F_USB_SendZlp -//* \brief Send zero length packet through the control endpoint -//*---------------------------------------------------------------------------- -void AT91F_USB_SendZlp(AT91PS_UDP pUdp) { - UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXPKTRDY); - while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) ); - UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); - while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); -} - -//*---------------------------------------------------------------------------- -//* \fn AT91F_USB_SendStall -//* \brief Stall the control endpoint -//*---------------------------------------------------------------------------- -void AT91F_USB_SendStall(AT91PS_UDP pUdp) { - UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_FORCESTALL); - while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_ISOERROR) ); - UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR) ); - while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)); -} - -//*---------------------------------------------------------------------------- -//* \fn AT91F_CDC_Enumerate -//* \brief This function is a callback invoked when a SETUP packet is received -//* problem: -//* 1. this is for USB endpoint0. the control endpoint. -//* 2. mixed with CDC ACM endpoint3 , interrupt, control endpoint -//*---------------------------------------------------------------------------- -void AT91F_CDC_Enumerate() { - byte_t bmRequestType, bRequest; - uint16_t wValue, wIndex, wLength, wStatus; - - if ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP) ) - return; - - bmRequestType = pUdp->UDP_FDR[AT91C_EP_CONTROL]; - bRequest = pUdp->UDP_FDR[AT91C_EP_CONTROL]; - wValue = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); - wValue |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); - wIndex = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); - wIndex |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); - wLength = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); - wLength |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); - - if (bmRequestType & 0x80) { // Data Phase Transfer Direction Device to Host - UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_DIR); - while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_DIR) ); - } - UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_RXSETUP); - while ( (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP) ); - - /* - if ( bRequest == MS_VENDOR_CODE) { - if ( bmRequestType == MS_WCID_GET_DESCRIPTOR ) { // C0 - if ( wIndex == MS_EXTENDED_COMPAT_ID ) { // 4 - //AT91F_USB_SendData(pUdp, CompatIDFeatureDescriptor, MIN(sizeof(CompatIDFeatureDescriptor), wLength)); - //return; - } - } - - if ( bmRequestType == MS_WCID_GET_FEATURE_DESCRIPTOR ) { //C1 - // if ( wIndex == MS_EXTENDED_PROPERTIES ) { // 5 - winusb bug with wIndex == interface index, so I just send it always) - //AT91F_USB_SendData(pUdp, OSprop, MIN(sizeof(OSprop), wLength)); - //return; - // } - } - } - */ - - // Handle supported standard device request Cf Table 9-3 in USB specification Rev 1.1 - switch ((bRequest << 8) | bmRequestType) { - case STD_GET_DESCRIPTOR: { - - if ( wValue == 0x100 ) // Return Device Descriptor - AT91F_USB_SendData(pUdp, devDescriptor, MIN(sizeof(devDescriptor), wLength)); - else if ( wValue == 0x200 ) // Return Configuration Descriptor - AT91F_USB_SendData(pUdp, cfgDescriptor, MIN(sizeof(cfgDescriptor), wLength)); - else if ( (wValue & 0xF00) == 0xF00) // Return BOS Descriptor - AT91F_USB_SendData(pUdp, bosDescriptor, MIN(sizeof(bosDescriptor), wLength)); - else if ( (wValue & 0x300) == 0x300) { // Return String Descriptor - - const char *strDescriptor = getStringDescriptor(wValue & 0xff); - if (strDescriptor != NULL) { - AT91F_USB_SendData(pUdp, strDescriptor, MIN(strDescriptor[0], wLength)); - } else { - AT91F_USB_SendStall(pUdp); - } - } else { - AT91F_USB_SendStall(pUdp); - } - } - break; - case STD_SET_ADDRESS: - AT91F_USB_SendZlp(pUdp); - pUdp->UDP_FADDR = (AT91C_UDP_FEN | (wValue & 0x7F) ); - pUdp->UDP_GLBSTATE = (wValue) ? AT91C_UDP_FADDEN : 0; - break; - case STD_SET_CONFIGURATION: - - /* - * Set or clear the device "configured" state. - * The LSB of wValue is the "Configuration Number". If this value is non-zero, - * it should be the same number as defined in the Configuration Descriptor; - * otherwise an error must have occurred. - * This device has only one configuration and its Config Number is CONF_NB (= 1). - */ - AT91F_USB_SendZlp(pUdp); - btConfiguration = wValue; - pUdp->UDP_GLBSTATE = (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN; - - // make sure we are not stalled - /* - UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT , AT91C_UDP_FORCESTALL); - UDP_CLEAR_EP_FLAGS(AT91C_EP_IN , AT91C_UDP_FORCESTALL); - UDP_CLEAR_EP_FLAGS(AT91C_EP_NOTIFY, AT91C_UDP_FORCESTALL); - */ - - // enable endpoints - pUdp->UDP_CSR[AT91C_EP_OUT] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : 0; - pUdp->UDP_CSR[AT91C_EP_IN] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; - pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0; - break; - case STD_GET_CONFIGURATION: - AT91F_USB_SendData(pUdp, (char *) &(btConfiguration), sizeof(btConfiguration)); - break; - case STD_GET_STATUS_ZERO: - wStatus = 0; // Device is Bus powered, remote wakeup disabled - AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); - break; - case STD_GET_STATUS_INTERFACE: - wStatus = 0; // reserved for future use - AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); - break; - case STD_GET_STATUS_ENDPOINT: - wStatus = 0; - wIndex &= 0x0F; - if ((pUdp->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= AT91C_EP_NOTIFY)) { - wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; - AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); - } - else if ((pUdp->UDP_GLBSTATE & AT91C_UDP_FADDEN) && (wIndex == AT91C_EP_CONTROL)) { - wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; - AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); - } else { - AT91F_USB_SendStall(pUdp); - } - break; - case STD_SET_FEATURE_ZERO: - AT91F_USB_SendStall(pUdp); - break; - case STD_SET_FEATURE_INTERFACE: - AT91F_USB_SendZlp(pUdp); - break; - case STD_SET_FEATURE_ENDPOINT: - wIndex &= 0x0F; - if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { - pUdp->UDP_CSR[wIndex] = 0; - AT91F_USB_SendZlp(pUdp); - } else { - AT91F_USB_SendStall(pUdp); - } - break; - case STD_CLEAR_FEATURE_ZERO: - AT91F_USB_SendStall(pUdp); - break; - case STD_CLEAR_FEATURE_INTERFACE: - AT91F_USB_SendZlp(pUdp); - break; - case STD_CLEAR_FEATURE_ENDPOINT: - wIndex &= 0x0F; - if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { - - if (wIndex == AT91C_EP_OUT) pUdp->UDP_CSR[AT91C_EP_OUT] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT); - else if (wIndex == AT91C_EP_IN) pUdp->UDP_CSR[AT91C_EP_IN] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); - else if (wIndex == AT91C_EP_NOTIFY) pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN); - - AT91F_USB_SendZlp(pUdp); - } else { - AT91F_USB_SendStall(pUdp); - } - break; - - // handle CDC class requests - case SET_LINE_CODING: { - /* - uint8_t i; - for ( i = 0 ; i < 7 ; i++ ) { - ((uint8_t*)&line)[i] = pUdp->UDP_FDR[AT91C_EP_CONTROL]; - } */ - // ignore SET_LINE_CODING... - while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RX_DATA_BK0) ); - UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_RX_DATA_BK0); - AT91F_USB_SendZlp(pUdp); - break; - } - case GET_LINE_CODING: - AT91F_USB_SendData(pUdp, (char *) &line, MIN(sizeof(line), wLength)); - break; - case SET_CONTROL_LINE_STATE: - btConnection = wValue; - AT91F_USB_SendZlp(pUdp); - break; - default: - AT91F_USB_SendStall(pUdp); - break; - } +/* + * at91sam7s USB CDC device implementation + * + * Copyright (c) 2012, Roel Verdult + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * based on the "Basic USB Example" from ATMEL (doc6123.pdf) + * + * @file usb_cdc.c + * @brief + */ + +#include "usb_cdc.h" +/* +AT91SAM7S256 USB Device Port +• Embedded 328-byte dual-port RAM for endpoints +• Four endpoints +– Endpoint 0: 8 bytes +– Endpoint 1 and 2: 64 bytes ping-pong +– Endpoint 3: 64 bytes +– Ping-pong Mode (two memory banks) for bulk endpoints +*/ + +// +#define AT91C_EP_CONTROL 0 +#define AT91C_EP_OUT 1 // cfg bulk out +#define AT91C_EP_IN 2 // cfg bulk in +#define AT91C_EP_NOTIFY 3 // cfg cdc notification interrup + +#define AT91C_EP_CONTROL_SIZE 8 +#define AT91C_EP_OUT_SIZE 64 +#define AT91C_EP_IN_SIZE 64 + + +// Section: USB Descriptors +#define USB_DESCRIPTOR_DEVICE 0x01 // DescriptorType for a Device Descriptor. +#define USB_DESCRIPTOR_CONFIGURATION 0x02 // DescriptorType for a Configuration Descriptor. +#define USB_DESCRIPTOR_STRING 0x03 // DescriptorType for a String Descriptor. +#define USB_DESCRIPTOR_INTERFACE 0x04 // DescriptorType for an Interface Descriptor. +#define USB_DESCRIPTOR_ENDPOINT 0x05 // DescriptorType for an Endpoint Descriptor. +#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06 // DescriptorType for a Device Qualifier. +#define USB_DESCRIPTOR_OTHER_SPEED 0x07 // DescriptorType for a Other Speed Configuration. +#define USB_DESCRIPTOR_INTERFACE_POWER 0x08 // DescriptorType for Interface Power. +#define USB_DESCRIPTOR_OTG 0x09 // DescriptorType for an OTG Descriptor. +#define USB_DESCRIPTOR_IAD 0x0B // DescriptorType for a Interface Association Descriptor +#define USB_DESCRIPTOR_TYPE_BO 0x0F // DescriptorType for a BOS Descriptor. + +/* Configuration Attributes */ +#define _DEFAULT (0x01<<7) //Default Value (Bit 7 is set) +#define _SELF (0x01<<6) //Self-powered (Supports if set) +#define _RWU (0x01<<5) //Remote Wakeup (Supports if set) +#define _HNP (0x01 << 1) //HNP (Supports if set) +#define _SRP (0x01) //SRP (Supports if set) + +/* Endpoint Transfer Type */ +#define _CTRL 0x00 //Control Transfer +#define _ISO 0x01 //Isochronous Transfer +#define _BULK 0x02 //Bulk Transfer +#define _INTERRUPT 0x03 //Interrupt Transfer + +// (bit7 | 0 = OUT, 1 = IN) +#define _EP_IN 0x80 +#define _EP_OUT 0x00 +#define _EP01_OUT 0x01 +#define _EP01_IN 0x81 +#define _EP02_OUT 0x02 +#define _EP02_IN 0x82 +#define _EP03_OUT 0x03 +#define _EP03_IN 0x83 + + +/* WCID specific Request Code */ +#define MS_OS_DESCRIPTOR_INDEX 0xEE +#define MS_VENDOR_CODE 0x1C +#define MS_EXTENDED_COMPAT_ID 0x04 +#define MS_EXTENDED_PROPERTIES 0x05 +#define MS_WCID_GET_DESCRIPTOR 0xC0 +#define MS_WCID_GET_FEATURE_DESCRIPTOR 0xC1 + +/* USB standard request code */ +#define STD_GET_STATUS_ZERO 0x0080 +#define STD_GET_STATUS_INTERFACE 0x0081 +#define STD_GET_STATUS_ENDPOINT 0x0082 + +#define STD_CLEAR_FEATURE_ZERO 0x0100 +#define STD_CLEAR_FEATURE_INTERFACE 0x0101 +#define STD_CLEAR_FEATURE_ENDPOINT 0x0102 + +#define STD_SET_FEATURE_ZERO 0x0300 +#define STD_SET_FEATURE_INTERFACE 0x0301 +#define STD_SET_FEATURE_ENDPOINT 0x0302 + +#define STD_SET_ADDRESS 0x0500 +#define STD_GET_DESCRIPTOR 0x0680 +#define STD_SET_DESCRIPTOR 0x0700 +#define STD_GET_CONFIGURATION 0x0880 +#define STD_SET_CONFIGURATION 0x0900 +#define STD_GET_INTERFACE 0x0A81 +#define STD_SET_INTERFACE 0x0B01 +#define STD_SYNCH_FRAME 0x0C82 + +/* CDC Class Specific Request Code */ +#define GET_LINE_CODING 0x21A1 +#define SET_LINE_CODING 0x2021 +#define SET_CONTROL_LINE_STATE 0x2221 + +AT91PS_UDP pUdp = AT91C_BASE_UDP; +uint8_t btConfiguration = 0; +uint8_t btConnection = 0; +uint8_t btReceiveBank = AT91C_UDP_RX_DATA_BK0; + +static const char devDescriptor[] = { + /* Device descriptor */ + 0x12, // Length + USB_DESCRIPTOR_DEVICE, // Descriptor Type (DEVICE) + 0x00,0x02, // Complies with USB Spec. Release (0200h = release 2.00) 0210 == release 2.10 + 2, // Device Class: Communication Device Class + 0, // Device Subclass: CDC class sub code ACM [ice 0x02 = win10 virtual comport ] + 0, // Device Protocol: CDC Device protocol (unused) + AT91C_EP_CONTROL_SIZE, // MaxPacketSize0 + 0xc4,0x9a, // Vendor ID [0x9ac4 = J. Westhues] + 0x8f,0x4b, // Product ID [0x4b8f = Proxmark-3 RFID Instrument] + 0x00,0x01, // BCD Device release number (1.00) + 1, // index Manufacturer + 2, // index Product + 3, // index SerialNumber + 1 // Number of Configs +}; + +static const char cfgDescriptor[] = { + + /* Configuration 1 descriptor */ + // ----------------------------- + 9, // Length + USB_DESCRIPTOR_CONFIGURATION, // Descriptor Type + (9+9+5+5+4+5+7+9+7+7), 0, // Total Length 2 EP + Control + 2, // Number of Interfaces + 1, // Index value of this Configuration (used in SetConfiguration from Host) + 0, // Configuration string index + _DEFAULT, // Attributes 0xA0 + 0xFA, // Max Power consumption + + // IAD to associate the one CDC interface + // -------------------------------------- +/* + 8, // Length + USB_DESCRIPTOR_IAD, // IAD_DESCRIPTOR (0x0B) + 0, // CDC_INT_INTERFACE NUMBER ( + 2, // IAD INTERFACE COUNT (two interfaces) + 2, // Function Class: CDC_CLASS + 2, // Function SubClass: ACM + 1, // Function Protocol: v.25term + 0, // iInterface +*/ + + /* Interface 0 Descriptor */ + /* CDC Communication Class Interface Descriptor Requirement for Notification*/ + // ----------------------------------------------------------- + 9, // Length + USB_DESCRIPTOR_INTERFACE, // Descriptor Type + 0, // Interface Number + 0, // Alternate Setting + 1, // Number of Endpoints in this interface + 2, // Interface Class code (Communication Interface Class) + 2, // Interface Subclass code (Abstract Control Model) + 1, // InterfaceProtocol (Common AT Commands, V.25term) + 0, // iInterface + + /* Header Functional Descriptor */ + 5, // Function Length + 0x24, // Descriptor type: CS_INTERFACE + 0, // Descriptor subtype: Header Functional Descriptor + 0x10,0x01, // bcd CDC:1.1 + + /* ACM Functional Descriptor */ + 4, // Function Length + 0x24, // Descriptor Type: CS_INTERFACE + 2, // Descriptor Subtype: Abstract Control Management Functional Descriptor + 2, // Capabilities D1, Device supports the request combination of Set_Line_Coding, Set_Control_Line_State, Get_Line_Coding, and the notification Serial_State + + /* Union Functional Descriptor */ + 5, // Function Length + 0x24, // Descriptor Type: CS_INTERFACE + 6, // Descriptor Subtype: Union Functional Descriptor + 0, // MasterInterface: Communication Class Interface + 1, // SlaveInterface0: Data Class Interface + + /* Call Management Functional Descriptor */ + 5, // Function Length + 0x24, // Descriptor Type: CS_INTERFACE + 1, // Descriptor Subtype: Call Management Functional Descriptor + 0, // Capabilities: Device sends/receives call management information only over the Communication Class interface. Device does not handle call management itself + 1, // Data Interface: Data Class Interface + + /* Protocol Functional Descriptor */ + /* + 6, + 0x24, // Descriptor Type: CS_INTERFACE + 0x0B, // Descriptor Subtype: Protocol Unit functional Descriptor + 0xDD, // constant uniq ID of unit + 0xFE, // protocol + */ + + /* CDC Notification Endpoint descriptor */ + // --------------------------------------- + 7, // Length + USB_DESCRIPTOR_ENDPOINT, // Descriptor Type + _EP03_IN, // EndpointAddress: Endpoint 03 - IN + _INTERRUPT, // Attributes + AT91C_EP_CONTROL_SIZE, 0x00, // MaxPacket Size: EP0 - 8 + 0xFF, // Interval polling + + + /* Interface 1 Descriptor */ + /* CDC Data Class Interface 1 Descriptor Requirement */ + 9, // Length + USB_DESCRIPTOR_INTERFACE, // Descriptor Type + 1, // Interface Number + 0, // Alternate Setting + 2, // Number of Endpoints + 0x0A, // Interface Class: CDC Data interface class + 0, // Interface Subclass: not used + 0, // Interface Protocol: No class specific protocol required (usb spec) + 0, // Interface + + /* Endpoint descriptor */ + 7, // Length + USB_DESCRIPTOR_ENDPOINT, // Descriptor Type + _EP01_OUT, // Endpoint Address: Endpoint 01 - OUT + _BULK, // Attributes: BULK + AT91C_EP_OUT_SIZE, 0x00, // MaxPacket Size: 64 bytes + 0, // Interval: ignored for bulk + + /* Endpoint descriptor */ + 7, // Length + USB_DESCRIPTOR_ENDPOINT, // Descriptor Type + _EP02_IN, // Endpoint Address: Endpoint 02 - IN + _BULK, // Attribute: BULK + AT91C_EP_IN_SIZE, 0x00, // MaxPacket Size: 64 bytes + 0 // Interval: ignored for bulk +}; + +// BOS descriptor +static const char bosDescriptor[] = { + 0x5, + USB_DESCRIPTOR_TYPE_BO, + 0xC, + 0x0, + 0x1, // 1 device capability + 0x7, + 0x10, //USB_DEVICE_CAPABITY_TYPE, + 0x2, + 0x2, // LPM capability bit set + 0x0, + 0x0, + 0x0 +}; + +// Microsoft OS Extended Configuration Compatible ID Descriptor +/* +static const char CompatIDFeatureDescriptor[] = { + 0x28, 0x00, 0x00, 0x00, // Descriptor Length 40bytes (0x28) + 0x00, 0x01, // Version ('1.0') + MS_EXTENDED_COMPAT_ID, 0x00, // Compatibility ID Descriptor Index 0x0004 + 0x01, // Number of sections. 0x1 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Reserved (7bytes) + //-----function section 1------ + 0x00, // Interface Number #0 + 0x01, // reserved (0x1) + 0x57, 0x49, 0x4E, 0x55, 0x53, 0x42, 0x00, 0x00, // Compatible ID ('WINUSB\0\0') (8bytes) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Sub-Compatible ID (8byte) + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 // Reserved (6bytes) +}; +*/ + +// Microsoft Extended Properties Feature Descriptor +/* +static const char OSprop[] = { + // u32 Descriptor Length (10+132+64+102 == 308 + 0x34, 0x01, 0, 0, + // u16 Version ('1.0') + 0, 1, + // u16 wIndex + MS_EXTENDED_PROPERTIES, 0, + // u16 wCount -- three section + 3, 0, + + //-----property section 1------ + // u32 size ( 14+40+78 == 132) + 132, 0, 0, 0, + // u32 type + 1, 0, 0, 0, // unicode string + // u16 namelen (20*2 = 40) + 40, 0, + // name DeviceInterfaceGUID + 'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,'I',0,'n',0,'t',0,'e',0,'r',0,'f',0,'a',0,'c',0,'e',0,'G',0,'U',0,'I',0,'D',0,0,0, + // u32 datalen (39*2 = 78) + 78, 0, 0, 0, + // data {4D36E978-E325-11CE-BFC1-08002BE10318} + '{',0,'4',0,'d',0,'3',0,'6',0,'e',0,'9',0,'7',0,'8',0,'-',0,'e',0,'3',0,'2',0,'5',0, + '-',0,'1',0,'1',0,'c',0,'e',0,'-',0,'b',0,'f',0,'c',0,'1',0,'-',0,'0',0,'8',0,'0',0, + '0',0,'2',0,'b',0,'e',0,'1',0,'0',0,'3',0,'1',0,'8',0,'}',0,0,0, + + //-----property section 2------ + // u32 size ( 14+12+38 == 64) + 64, 0, 0, 0, + // u32 type + 1, 0, 0, 0, // unicode string + // u16 namelen (12) + 12, 0, + // name Label + 'L',0,'a',0,'b',0,'e',0,'l',0,0,0, + // u32 datalen ( 19*2 = 38 ) + 38, 0, 0, 0, + // data 'Awesome PM3 Device' + 'A',0,'w',0,'e',0,'s',0,'o',0,'m',0,'e',0,' ',0,'P',0,'M',0,'3',0,' ',0,'D',0,'e',0,'v',0,'i',0,'c',0,'e',0,0,0, + + //-----property section 3------ + // u32 size ( 14+12+76 == 102) + 102, 0, 0, 0, + // u32 type + 2, 0, 0, 0, //Unicode string with environment variables + // u16 namelen (12) + 12, 0, + // name Icons + 'I',0,'c',0,'o',0,'n',0,'s',0,0,0, + // u32 datalen ( 38*2 == 76) + 76, 0, 0, 0, + // data '%SystemRoot%\\system32\\Shell32.dll,-13' + '%',0,'S',0,'y',0,'s',0,'t',0,'e',0,'m',0,'R',0,'o',0,'o',0,'t',0,'%',0, + '\\',0,'s',0,'y',0,'s',0,'t',0,'e',0,'m',0,'3',0,'2',0,'\\',0, + 'S',0,'h',0,'e',0,'l',0,'l',0,'3',0,'2',0,'.',0,'d',0,'l',0,'l',0,',',0, + '-',0,'1',0,'3',0,0,0 +}; + +*/ + +static const char StrLanguageCodes[] = { + 4, // Length + 0x03, // Type is string + 0x09, 0x04 // supported language Code 0 = 0x0409 (English) +}; + +// Note: ModemManager (Linux) ignores Proxmark3 devices by matching the +// manufacturer string "proxmark.org". Don't change this. +// or use the blacklisting file. +static const char StrManufacturer[] = { + 26, // Length + 0x03, // Type is string + 'p',0,'r',0,'o',0,'x',0,'m',0,'a',0,'r',0,'k',0,'.',0,'o',0,'r',0,'g',0, +}; + +static const char StrProduct[] = { + 20, // Length + 0x03, // Type is string + 'p',0,'r',0,'o',0,'x',0,'m',0,'a',0,'r',0,'k',0,'3',0 +}; + +static const char StrSerialNumber[] = { + 14, // Length + 0x03, // Type is string + 'i',0,'c',0,'e',0,'m',0,'a',0,'n',0 +}; + +// size includes their own field. +static const char StrMS_OSDescriptor[] = { + 18, // length 0x12 + 0x03, // Type is string + 'M',0,'S',0,'F',0,'T',0,'1',0,'0',0,'0',0,MS_VENDOR_CODE,0 +}; + +const char* getStringDescriptor(uint8_t idx) { + switch(idx) { + case 0: return StrLanguageCodes; + case 1: return StrManufacturer; + case 2: return StrProduct; + case 3: return StrSerialNumber; + case MS_OS_DESCRIPTOR_INDEX: return StrMS_OSDescriptor; + default: + return(NULL); + } +} + +// Bitmap for all status bits in CSR which must be written as 1 to cause no effect +#define REG_NO_EFFECT_1_ALL AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RX_DATA_BK1 \ + |AT91C_UDP_STALLSENT | AT91C_UDP_RXSETUP \ + |AT91C_UDP_TXCOMP + +// Clear flags in the UDP_CSR register and waits for synchronization +#define UDP_CLEAR_EP_FLAGS(endpoint, flags) { \ + volatile unsigned int reg; \ + reg = pUdp->UDP_CSR[(endpoint)]; \ + reg |= REG_NO_EFFECT_1_ALL; \ + reg &= ~(flags); \ + pUdp->UDP_CSR[(endpoint)] = reg; \ +} \ + +// reset flags in the UDP_CSR register and waits for synchronization +#define UDP_SET_EP_FLAGS(endpoint, flags) { \ + volatile unsigned int reg; \ + reg = pUdp->UDP_CSR[(endpoint)]; \ + reg |= REG_NO_EFFECT_1_ALL; \ + reg |= (flags); \ + pUdp->UDP_CSR[(endpoint)] = reg; \ +} \ + + +typedef struct { + uint32_t BitRate; + uint8_t Format; + uint8_t ParityType; + uint8_t DataBits; +} AT91S_CDC_LINE_CODING, *AT91PS_CDC_LINE_CODING; + +AT91S_CDC_LINE_CODING line = { + 115200, // baudrate + 0, // 1 Stop Bit + 0, // None Parity + 8}; // 8 Data bits + +static void SpinDelay(int ms) { + int us = ms * 1000; + int ticks = (48 * us) >> 10; + + // Borrow a PWM unit for my real-time clock + AT91C_BASE_PWMC->PWMC_ENA = PWM_CHANNEL(0); + + // 48 MHz / 1024 gives 46.875 kHz + AT91C_BASE_PWMC_CH0->PWMC_CMR = PWM_CH_MODE_PRESCALER(10); + AT91C_BASE_PWMC_CH0->PWMC_CDTYR = 0; + AT91C_BASE_PWMC_CH0->PWMC_CPRDR = 0xffff; + + uint16_t start = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + + for(;;) { + uint16_t now = AT91C_BASE_PWMC_CH0->PWMC_CCNTR; + if (now == (uint16_t)(start + ticks)) + return; + + WDT_HIT(); + } +} + +//*---------------------------------------------------------------------------- +//* \fn usb_disable +//* \brief This function deactivates the USB device +//*---------------------------------------------------------------------------- +void usb_disable() { + // Disconnect the USB device + AT91C_BASE_PIOA->PIO_ODR = GPIO_USB_PU; + + // Clear all lingering interrupts + if (pUdp->UDP_ISR & AT91C_UDP_ENDBUSRES) { + pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; + } +} + +//*---------------------------------------------------------------------------- +//* \fn usb_enable +//* \brief This function Activates the USB device +//*---------------------------------------------------------------------------- +void usb_enable() { + // Set the PLL USB Divider + AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1 ; + + // Specific Chip USB Initialisation + // Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock + AT91C_BASE_PMC->PMC_SCER |= AT91C_PMC_UDP; + AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP); + + AT91C_BASE_UDP->UDP_FADDR = 0; + AT91C_BASE_UDP->UDP_GLBSTATE = 0; + + // Enable UDP PullUp (USB_DP_PUP) : enable & Clear of the corresponding PIO + // Set in PIO mode and Configure in Output + AT91C_BASE_PIOA->PIO_PER = GPIO_USB_PU; // Set in PIO mode + AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; // Configure as Output + + // Clear for set the Pullup resistor + AT91C_BASE_PIOA->PIO_CODR = GPIO_USB_PU; + + // Disconnect and reconnect USB controller for 100ms + usb_disable(); + + SpinDelay(100); + // Wait for a short while + //for (volatile size_t i=0; i<0x100000; i++) {}; + + // Reconnect USB reconnect + AT91C_BASE_PIOA->PIO_SODR = GPIO_USB_PU; + AT91C_BASE_PIOA->PIO_OER = GPIO_USB_PU; +} + +//*---------------------------------------------------------------------------- +//* \fn usb_check +//* \brief Test if the device is configured and handle enumeration +//*---------------------------------------------------------------------------- +static int usb_reconnect = 0; +static int usb_configured = 0; +void SetUSBreconnect(int value) { + usb_reconnect = value; +} +int GetUSBreconnect(void) { + return usb_reconnect; +} +void SetUSBconfigured(int value) { + usb_configured = value; +} +int GetUSBconfigured(void){ + return usb_configured; +} + +bool usb_check() { + + /* + // reconnected ONCE and + if ( !USB_ATTACHED() ){ + usb_reconnect = 1; + return false; + } + + // only one time after USB been disengaged and re-engaged + if ( USB_ATTACHED() && usb_reconnect == 1 ) { + + if ( usb_configured == 0) { + usb_disable(); + usb_enable(); + + AT91F_CDC_Enumerate(); + + usb_configured = 1; + return false; + } + } + */ + + // interrupt status register + AT91_REG isr = pUdp->UDP_ISR; + + // end of bus reset + if (isr & AT91C_UDP_ENDBUSRES) { + pUdp->UDP_ICR = AT91C_UDP_ENDBUSRES; + // reset all endpoints + pUdp->UDP_RSTEP = (unsigned int)-1; + pUdp->UDP_RSTEP = 0; + // Enable the function + pUdp->UDP_FADDR = AT91C_UDP_FEN; + // Configure endpoint 0 (enable control endpoint) + pUdp->UDP_CSR[AT91C_EP_CONTROL] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_CTRL); + } + else if (isr & AT91C_UDP_EPINT0) { + pUdp->UDP_ICR = AT91C_UDP_EPINT0; + AT91F_CDC_Enumerate(); + } + /* + else if (isr & AT91C_UDP_EPINT3 ) { + pUdp->UDP_ICR = AT91C_UDP_EPINT3; + AT91F_CDC_Enumerate(); + //pUdp->UDP_ICR |= AT91C_UDP_EPINT3; + } + */ + return (btConfiguration) ? true : false; +} + +bool usb_poll() { + if (!usb_check()) return false; + return (pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank); +} + +/** + In github PR #129, some users appears to get a false positive from + usb_poll, which returns true, but the usb_read operation + still returns 0. + This check is basically the same as above, but also checks + that the length available to read is non-zero, thus hopefully fixes the + bug. +**/ +bool usb_poll_validate_length() { + if (!usb_check()) return false; + if (!(pUdp->UDP_CSR[AT91C_EP_OUT] & btReceiveBank)) return false; + return ((pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16) > 0; +} + +//*---------------------------------------------------------------------------- +//* \fn usb_read +//* \brief Read available data from Endpoint 1 OUT (host to device) +//*---------------------------------------------------------------------------- +uint32_t usb_read(byte_t* data, size_t len) { + + if ( len == 0 ) return 0; + + uint8_t bank = btReceiveBank; + uint32_t packetSize, nbBytesRcv = 0; + uint32_t time_out = 0; + + while (len) { + if (!usb_check()) break; + + if ( pUdp->UDP_CSR[AT91C_EP_OUT] & bank ) { + + packetSize = (pUdp->UDP_CSR[AT91C_EP_OUT] & AT91C_UDP_RXBYTECNT) >> 16; + packetSize = MIN( packetSize, len); + len -= packetSize; + while (packetSize--) + data[nbBytesRcv++] = pUdp->UDP_FDR[AT91C_EP_OUT]; + + // flip bank + UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT, bank) + + if (bank == AT91C_UDP_RX_DATA_BK0) + bank = AT91C_UDP_RX_DATA_BK1; + else + bank = AT91C_UDP_RX_DATA_BK0; + } + if (time_out++ == 0x1fff) break; + } + + btReceiveBank = bank; + return nbBytesRcv; +} + +//*---------------------------------------------------------------------------- +//* \fn usb_write +//* \brief Send through endpoint 2 (device to host) +//*---------------------------------------------------------------------------- +uint32_t usb_write(const byte_t* data, const size_t len) { + + if (!len) return 0; + if (!usb_check()) return 0; + + // can we write? + if ( (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXPKTRDY) != 0 ) return 0; + + size_t length = len; + uint32_t cpt = 0; + + + // send first chunk + cpt = MIN(length, AT91C_EP_IN_SIZE); + length -= cpt; + while (cpt--) { + pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + } + + UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); + + while (length) { + // Send next chunk + cpt = MIN(length, AT91C_EP_IN_SIZE); + length -= cpt; + while (cpt--) { + pUdp->UDP_FDR[AT91C_EP_IN] = *data++; + } + + // Wait for previous chunk to be sent + // (iceman) when is the bankswapping done? + while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { + if (!usb_check()) return length; + } + + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); + UDP_SET_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXPKTRDY); + + } + + // Wait for the end of transfer + while (!(pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP)) { + if (!usb_check()) return length; + } + + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_IN] & AT91C_UDP_TXCOMP); + + return length; +} + +//*---------------------------------------------------------------------------- +//* \fn AT91F_USB_SendData +//* \brief Send Data through the control endpoint +//*---------------------------------------------------------------------------- +void AT91F_USB_SendData(AT91PS_UDP pUdp, const char *pData, uint32_t length) { + uint32_t cpt = 0; + AT91_REG csr; + + do { + cpt = MIN(length, AT91C_EP_CONTROL_SIZE); + length -= cpt; + + while (cpt--) + pUdp->UDP_FDR[AT91C_EP_CONTROL] = *pData++; + + if (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) { + UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); + } + + UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXPKTRDY); + + do { + csr = pUdp->UDP_CSR[AT91C_EP_CONTROL]; + // Data IN stage has been stopped by a status OUT + if ( csr & AT91C_UDP_RX_DATA_BK0) { + + UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_RX_DATA_BK0) + return; + } + } while ( !(csr & AT91C_UDP_TXCOMP) ); + + } while (length); + + if (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) { + UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); + } +} + + +//*---------------------------------------------------------------------------- +//* \fn AT91F_USB_SendZlp +//* \brief Send zero length packet through the control endpoint +//*---------------------------------------------------------------------------- +void AT91F_USB_SendZlp(AT91PS_UDP pUdp) { + UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXPKTRDY); + while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP) ); + UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_TXCOMP); + while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_TXCOMP); +} + +//*---------------------------------------------------------------------------- +//* \fn AT91F_USB_SendStall +//* \brief Stall the control endpoint +//*---------------------------------------------------------------------------- +void AT91F_USB_SendStall(AT91PS_UDP pUdp) { + UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_FORCESTALL); + while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_ISOERROR) ); + UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR) ); + while (pUdp->UDP_CSR[AT91C_EP_CONTROL] & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)); +} + +//*---------------------------------------------------------------------------- +//* \fn AT91F_CDC_Enumerate +//* \brief This function is a callback invoked when a SETUP packet is received +//* problem: +//* 1. this is for USB endpoint0. the control endpoint. +//* 2. mixed with CDC ACM endpoint3 , interrupt, control endpoint +//*---------------------------------------------------------------------------- +void AT91F_CDC_Enumerate() { + byte_t bmRequestType, bRequest; + uint16_t wValue, wIndex, wLength, wStatus; + + if ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP) ) + return; + + bmRequestType = pUdp->UDP_FDR[AT91C_EP_CONTROL]; + bRequest = pUdp->UDP_FDR[AT91C_EP_CONTROL]; + wValue = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wValue |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); + wIndex = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wIndex |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); + wLength = (pUdp->UDP_FDR[AT91C_EP_CONTROL] & 0xFF); + wLength |= (pUdp->UDP_FDR[AT91C_EP_CONTROL] << 8); + + if (bmRequestType & 0x80) { // Data Phase Transfer Direction Device to Host + UDP_SET_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_DIR); + while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_DIR) ); + } + UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_RXSETUP); + while ( (pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RXSETUP) ); + + /* + if ( bRequest == MS_VENDOR_CODE) { + if ( bmRequestType == MS_WCID_GET_DESCRIPTOR ) { // C0 + if ( wIndex == MS_EXTENDED_COMPAT_ID ) { // 4 + //AT91F_USB_SendData(pUdp, CompatIDFeatureDescriptor, MIN(sizeof(CompatIDFeatureDescriptor), wLength)); + //return; + } + } + + if ( bmRequestType == MS_WCID_GET_FEATURE_DESCRIPTOR ) { //C1 + // if ( wIndex == MS_EXTENDED_PROPERTIES ) { // 5 - winusb bug with wIndex == interface index, so I just send it always) + //AT91F_USB_SendData(pUdp, OSprop, MIN(sizeof(OSprop), wLength)); + //return; + // } + } + } + */ + + // Handle supported standard device request Cf Table 9-3 in USB specification Rev 1.1 + switch ((bRequest << 8) | bmRequestType) { + case STD_GET_DESCRIPTOR: { + + if ( wValue == 0x100 ) // Return Device Descriptor + AT91F_USB_SendData(pUdp, devDescriptor, MIN(sizeof(devDescriptor), wLength)); + else if ( wValue == 0x200 ) // Return Configuration Descriptor + AT91F_USB_SendData(pUdp, cfgDescriptor, MIN(sizeof(cfgDescriptor), wLength)); + else if ( (wValue & 0xF00) == 0xF00) // Return BOS Descriptor + AT91F_USB_SendData(pUdp, bosDescriptor, MIN(sizeof(bosDescriptor), wLength)); + else if ( (wValue & 0x300) == 0x300) { // Return String Descriptor + + const char *strDescriptor = getStringDescriptor(wValue & 0xff); + if (strDescriptor != NULL) { + AT91F_USB_SendData(pUdp, strDescriptor, MIN(strDescriptor[0], wLength)); + } else { + AT91F_USB_SendStall(pUdp); + } + } else { + AT91F_USB_SendStall(pUdp); + } + } + break; + case STD_SET_ADDRESS: + AT91F_USB_SendZlp(pUdp); + pUdp->UDP_FADDR = (AT91C_UDP_FEN | (wValue & 0x7F) ); + pUdp->UDP_GLBSTATE = (wValue) ? AT91C_UDP_FADDEN : 0; + break; + case STD_SET_CONFIGURATION: + + /* + * Set or clear the device "configured" state. + * The LSB of wValue is the "Configuration Number". If this value is non-zero, + * it should be the same number as defined in the Configuration Descriptor; + * otherwise an error must have occurred. + * This device has only one configuration and its Config Number is CONF_NB (= 1). + */ + AT91F_USB_SendZlp(pUdp); + btConfiguration = wValue; + pUdp->UDP_GLBSTATE = (wValue) ? AT91C_UDP_CONFG : AT91C_UDP_FADDEN; + + // make sure we are not stalled + /* + UDP_CLEAR_EP_FLAGS(AT91C_EP_OUT , AT91C_UDP_FORCESTALL); + UDP_CLEAR_EP_FLAGS(AT91C_EP_IN , AT91C_UDP_FORCESTALL); + UDP_CLEAR_EP_FLAGS(AT91C_EP_NOTIFY, AT91C_UDP_FORCESTALL); + */ + + // enable endpoints + pUdp->UDP_CSR[AT91C_EP_OUT] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT) : 0; + pUdp->UDP_CSR[AT91C_EP_IN] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN) : 0; + pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (wValue) ? (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN) : 0; + break; + case STD_GET_CONFIGURATION: + AT91F_USB_SendData(pUdp, (char *) &(btConfiguration), sizeof(btConfiguration)); + break; + case STD_GET_STATUS_ZERO: + wStatus = 0; // Device is Bus powered, remote wakeup disabled + AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); + break; + case STD_GET_STATUS_INTERFACE: + wStatus = 0; // reserved for future use + AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); + break; + case STD_GET_STATUS_ENDPOINT: + wStatus = 0; + wIndex &= 0x0F; + if ((pUdp->UDP_GLBSTATE & AT91C_UDP_CONFG) && (wIndex <= AT91C_EP_NOTIFY)) { + wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; + AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); + } + else if ((pUdp->UDP_GLBSTATE & AT91C_UDP_FADDEN) && (wIndex == AT91C_EP_CONTROL)) { + wStatus = (pUdp->UDP_CSR[wIndex] & AT91C_UDP_EPEDS) ? 0 : 1; + AT91F_USB_SendData(pUdp, (char *) &wStatus, sizeof(wStatus)); + } else { + AT91F_USB_SendStall(pUdp); + } + break; + case STD_SET_FEATURE_ZERO: + AT91F_USB_SendStall(pUdp); + break; + case STD_SET_FEATURE_INTERFACE: + AT91F_USB_SendZlp(pUdp); + break; + case STD_SET_FEATURE_ENDPOINT: + wIndex &= 0x0F; + if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { + pUdp->UDP_CSR[wIndex] = 0; + AT91F_USB_SendZlp(pUdp); + } else { + AT91F_USB_SendStall(pUdp); + } + break; + case STD_CLEAR_FEATURE_ZERO: + AT91F_USB_SendStall(pUdp); + break; + case STD_CLEAR_FEATURE_INTERFACE: + AT91F_USB_SendZlp(pUdp); + break; + case STD_CLEAR_FEATURE_ENDPOINT: + wIndex &= 0x0F; + if ((wValue == 0) && (wIndex >= AT91C_EP_OUT) && (wIndex <= AT91C_EP_NOTIFY)) { + + if (wIndex == AT91C_EP_OUT) pUdp->UDP_CSR[AT91C_EP_OUT] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT); + else if (wIndex == AT91C_EP_IN) pUdp->UDP_CSR[AT91C_EP_IN] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN); + else if (wIndex == AT91C_EP_NOTIFY) pUdp->UDP_CSR[AT91C_EP_NOTIFY] = (AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN); + + AT91F_USB_SendZlp(pUdp); + } else { + AT91F_USB_SendStall(pUdp); + } + break; + + // handle CDC class requests + case SET_LINE_CODING: { + /* + uint8_t i; + for ( i = 0 ; i < 7 ; i++ ) { + ((uint8_t*)&line)[i] = pUdp->UDP_FDR[AT91C_EP_CONTROL]; + } */ + // ignore SET_LINE_CODING... + while ( !(pUdp->UDP_CSR[AT91C_EP_CONTROL] & AT91C_UDP_RX_DATA_BK0) ); + UDP_CLEAR_EP_FLAGS(AT91C_EP_CONTROL, AT91C_UDP_RX_DATA_BK0); + AT91F_USB_SendZlp(pUdp); + break; + } + case GET_LINE_CODING: + AT91F_USB_SendData(pUdp, (char *) &line, MIN(sizeof(line), wLength)); + break; + case SET_CONTROL_LINE_STATE: + btConnection = wValue; + AT91F_USB_SendZlp(pUdp); + break; + default: + AT91F_USB_SendStall(pUdp); + break; + } } \ No newline at end of file diff --git a/common/usb_cdc.h b/common/usb_cdc.h index 6d471db92..fcac556fd 100644 --- a/common/usb_cdc.h +++ b/common/usb_cdc.h @@ -1,63 +1,63 @@ -/* - * at91sam7s USB CDC device implementation - * - * Copyright (c) 2012, Roel Verdult - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. Neither the name of the copyright holders nor the - * names of its contributors may be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY - * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED - * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY - * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND - * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * based on the "Basic USB Example" from ATMEL (doc6123.pdf) - * - * @file usb_cdc.c - * @brief - */ - -#ifndef _USB_CDC_H_ -#define _USB_CDC_H_ - -#include -#include "at91sam7s512.h" -#include "config_gpio.h" -#include "proxmark3.h" // USB_CONNECT() -#include "common.h" - -extern void usb_disable(); -extern void usb_enable(); -extern bool usb_check(); -extern bool usb_poll(); -extern bool usb_poll_validate_length(); -extern uint32_t usb_read(byte_t* data, size_t len); -extern uint32_t usb_write(const byte_t* data, const size_t len); - -extern void SetUSBreconnect(int value); -extern int GetUSBreconnect(void); -extern void SetUSBconfigured(int value); -extern int GetUSBconfigured(void); - -extern void AT91F_USB_SendData(AT91PS_UDP pUdp, const char *pData, uint32_t length); -extern void AT91F_USB_SendZlp(AT91PS_UDP pUdp); -extern void AT91F_USB_SendStall(AT91PS_UDP pUdp); -extern void AT91F_CDC_Enumerate(); - -#endif // _USB_CDC_H_ - +/* + * at91sam7s USB CDC device implementation + * + * Copyright (c) 2012, Roel Verdult + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the copyright holders nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ''AS IS'' AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * based on the "Basic USB Example" from ATMEL (doc6123.pdf) + * + * @file usb_cdc.c + * @brief + */ + +#ifndef _USB_CDC_H_ +#define _USB_CDC_H_ + +#include +#include "at91sam7s512.h" +#include "config_gpio.h" +#include "proxmark3.h" // USB_CONNECT() +#include "common.h" + +extern void usb_disable(); +extern void usb_enable(); +extern bool usb_check(); +extern bool usb_poll(); +extern bool usb_poll_validate_length(); +extern uint32_t usb_read(byte_t* data, size_t len); +extern uint32_t usb_write(const byte_t* data, const size_t len); + +extern void SetUSBreconnect(int value); +extern int GetUSBreconnect(void); +extern void SetUSBconfigured(int value); +extern int GetUSBconfigured(void); + +extern void AT91F_USB_SendData(AT91PS_UDP pUdp, const char *pData, uint32_t length); +extern void AT91F_USB_SendZlp(AT91PS_UDP pUdp); +extern void AT91F_USB_SendStall(AT91PS_UDP pUdp); +extern void AT91F_CDC_Enumerate(); + +#endif // _USB_CDC_H_ + diff --git a/tools/mfkey/crapto1.c b/tools/mfkey/crapto1.c index ea7f60bba..203efeb4f 100755 --- a/tools/mfkey/crapto1.c +++ b/tools/mfkey/crapto1.c @@ -1,574 +1,574 @@ -/* crapto1.c - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, US$ - - Copyright (C) 2008-2014 bla -*/ -#include "crapto1.h" -#include - -#if !defined LOWMEM && defined __GNUC__ -static uint8_t filterlut[1 << 20]; -static void __attribute__((constructor)) fill_lut() -{ - uint32_t i; - for(i = 0; i < 1 << 20; ++i) - filterlut[i] = filter(i); -} -#define filter(x) (filterlut[(x) & 0xfffff]) -#endif - - - -typedef struct bucket { - uint32_t *head; - uint32_t *bp; -} bucket_t; - -typedef bucket_t bucket_array_t[2][0x100]; - -typedef struct bucket_info { - struct { - uint32_t *head, *tail; - } bucket_info[2][0x100]; - uint32_t numbuckets; - } bucket_info_t; - - -static void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, - uint32_t* const ostart, uint32_t* const ostop, - bucket_info_t *bucket_info, bucket_array_t bucket) -{ - uint32_t *p1, *p2; - uint32_t *start[2]; - uint32_t *stop[2]; - - start[0] = estart; - stop[0] = estop; - start[1] = ostart; - stop[1] = ostop; - - // init buckets to be empty - for (uint32_t i = 0; i < 2; i++) { - for (uint32_t j = 0x00; j <= 0xff; j++) { - bucket[i][j].bp = bucket[i][j].head; - } - } - - // sort the lists into the buckets based on the MSB (contribution bits) - for (uint32_t i = 0; i < 2; i++) { - for (p1 = start[i]; p1 <= stop[i]; p1++) { - uint32_t bucket_index = (*p1 & 0xff000000) >> 24; - *(bucket[i][bucket_index].bp++) = *p1; - } - } - - - // write back intersecting buckets as sorted list. - // fill in bucket_info with head and tail of the bucket contents in the list and number of non-empty buckets. - uint32_t nonempty_bucket; - for (uint32_t i = 0; i < 2; i++) { - p1 = start[i]; - nonempty_bucket = 0; - for (uint32_t j = 0x00; j <= 0xff; j++) { - if (bucket[0][j].bp != bucket[0][j].head && bucket[1][j].bp != bucket[1][j].head) { // non-empty intersecting buckets only - bucket_info->bucket_info[i][nonempty_bucket].head = p1; - for (p2 = bucket[i][j].head; p2 < bucket[i][j].bp; *p1++ = *p2++); - bucket_info->bucket_info[i][nonempty_bucket].tail = p1 - 1; - nonempty_bucket++; - } - } - bucket_info->numbuckets = nonempty_bucket; - } -} - - -/** update_contribution - * helper, calculates the partial linear feedback contributions and puts in MSB - */ -static inline void update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) -{ - uint32_t p = *item >> 25; - - p = p << 1 | parity(*item & mask1); - p = p << 1 | parity(*item & mask2); - *item = p << 24 | (*item & 0xffffff); -} - -/** extend_table - * using a bit of the keystream extend the table of possible lfsr states - */ -static inline void extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) -{ - in <<= 24; - for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) - if(filter(*tbl) ^ filter(*tbl | 1)) { - *tbl |= filter(*tbl) ^ bit; - update_contribution(tbl, m1, m2); - *tbl ^= in; - } else if(filter(*tbl) == bit) { - *++*end = tbl[1]; - tbl[1] = tbl[0] | 1; - update_contribution(tbl, m1, m2); - *tbl++ ^= in; - update_contribution(tbl, m1, m2); - *tbl ^= in; - } else - *tbl-- = *(*end)--; -} -/** extend_table_simple - * using a bit of the keystream extend the table of possible lfsr states - */ -static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) -{ - for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) { - if(filter(*tbl) ^ filter(*tbl | 1)) { // replace - *tbl |= filter(*tbl) ^ bit; - } else if(filter(*tbl) == bit) { // insert - *++*end = *++tbl; - *tbl = tbl[-1] | 1; - } else { // drop - *tbl-- = *(*end)--; - } - } -} -/** recover - * recursively narrow down the search space, 4 bits of keystream at a time - */ -static struct Crypto1State* -recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, - uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, - struct Crypto1State *sl, uint32_t in, bucket_array_t bucket) -{ - uint32_t *o, *e; - bucket_info_t bucket_info; - - if(rem == -1) { - for(e = e_head; e <= e_tail; ++e) { - *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); - for(o = o_head; o <= o_tail; ++o, ++sl) { - sl->even = *o; - sl->odd = *e ^ parity(*o & LF_POLY_ODD); - sl[1].odd = sl[1].even = 0; - } - } - return sl; - } - - for(uint32_t i = 0; i < 4 && rem--; i++) { - oks >>= 1; - eks >>= 1; - in >>= 2; - extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0); - if(o_head > o_tail) - return sl; - - extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3); - if(e_head > e_tail) - return sl; - } - - bucket_sort_intersect(e_head, e_tail, o_head, o_tail, &bucket_info, bucket); - - for (int i = bucket_info.numbuckets - 1; i >= 0; i--) { - sl = recover(bucket_info.bucket_info[1][i].head, bucket_info.bucket_info[1][i].tail, oks, - bucket_info.bucket_info[0][i].head, bucket_info.bucket_info[0][i].tail, eks, - rem, sl, in, bucket); - } - - return sl; -} -/** lfsr_recovery - * recover the state of the lfsr given 32 bits of the keystream - * additionally you can use the in parameter to specify the value - * that was fed into the lfsr at the time the keystream was generated - */ -struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) -{ - struct Crypto1State *statelist; - uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; - uint32_t *even_head = 0, *even_tail = 0, eks = 0; - int i; - - // split the keystream into an odd and even part - for(i = 31; i >= 0; i -= 2) - oks = oks << 1 | BEBIT(ks2, i); - for(i = 30; i >= 0; i -= 2) - eks = eks << 1 | BEBIT(ks2, i); - - odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); - even_head = even_tail = malloc(sizeof(uint32_t) << 21); - statelist = malloc(sizeof(struct Crypto1State) << 18); - if(!odd_tail-- || !even_tail-- || !statelist) { - free(statelist); - statelist = 0; - goto out; - } - - statelist->odd = statelist->even = 0; - - // allocate memory for out of place bucket_sort - bucket_array_t bucket; - - for (uint32_t i = 0; i < 2; i++) { - for (uint32_t j = 0; j <= 0xff; j++) { - bucket[i][j].head = malloc(sizeof(uint32_t)<<14); - if (!bucket[i][j].head) { - goto out; - } - } - } - - // initialize statelists: add all possible states which would result into the rightmost 2 bits of the keystream - for(i = 1 << 20; i >= 0; --i) { - if(filter(i) == (oks & 1)) - *++odd_tail = i; - if(filter(i) == (eks & 1)) - *++even_tail = i; - } - - // extend the statelists. Look at the next 8 Bits of the keystream (4 Bit each odd and even): - for(i = 0; i < 4; i++) { - extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); - extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); - } - - // the statelists now contain all states which could have generated the last 10 Bits of the keystream. - // 22 bits to go to recover 32 bits in total. From now on, we need to take the "in" - // parameter into account. - in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); // Byte swapping - recover(odd_head, odd_tail, oks, even_head, even_tail, eks, 11, statelist, in << 1, bucket); - -out: - for (uint32_t i = 0; i < 2; i++) - for (uint32_t j = 0; j <= 0xff; j++) - free(bucket[i][j].head); - free(odd_head); - free(even_head); - return statelist; -} - -static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214, - 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, - 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA}; -static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60, - 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, - 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, - 0x7EC7EE90, 0x7F63F748, 0x79117020}; -static const uint32_t T1[] = { - 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, - 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, - 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, - 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C}; -static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0, - 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, - 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, - 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, - 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, - 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0}; -static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD}; -static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0}; -/** Reverse 64 bits of keystream into possible cipher states - * Variation mentioned in the paper. Somewhat optimized version - */ -struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) -{ - struct Crypto1State *statelist, *sl; - uint8_t oks[32], eks[32], hi[32]; - uint32_t low = 0, win = 0; - uint32_t *tail, table[1 << 16]; - int i, j; - - sl = statelist = malloc(sizeof(struct Crypto1State) << 4); - if(!sl) - return 0; - sl->odd = sl->even = 0; - - for(i = 30; i >= 0; i -= 2) { - oks[i >> 1] = BEBIT(ks2, i); - oks[16 + (i >> 1)] = BEBIT(ks3, i); - } - for(i = 31; i >= 0; i -= 2) { - eks[i >> 1] = BEBIT(ks2, i); - eks[16 + (i >> 1)] = BEBIT(ks3, i); - } - - for(i = 0xfffff; i >= 0; --i) { - if (filter(i) != oks[0]) - continue; - - *(tail = table) = i; - for(j = 1; tail >= table && j < 29; ++j) - extend_table_simple(table, &tail, oks[j]); - - if(tail < table) - continue; - - for(j = 0; j < 19; ++j) - low = low << 1 | parity(i & S1[j]); - for(j = 0; j < 32; ++j) - hi[j] = parity(i & T1[j]); - - for(; tail >= table; --tail) { - for(j = 0; j < 3; ++j) { - *tail = *tail << 1; - *tail |= parity((i & C1[j]) ^ (*tail & C2[j])); - if(filter(*tail) != oks[29 + j]) - goto continue2; - } - - for(j = 0; j < 19; ++j) - win = win << 1 | parity(*tail & S2[j]); - - win ^= low; - for(j = 0; j < 32; ++j) { - win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); - if(filter(win) != eks[j]) - goto continue2; - } - - *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); - sl->odd = *tail ^ parity(LF_POLY_ODD & win); - sl->even = win; - ++sl; - sl->odd = sl->even = 0; - continue2:; - } - } - return statelist; -} - -/** lfsr_rollback_bit - * Rollback the shift register in order to get previous states - */ -uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) -{ - int out; - uint8_t ret; - uint32_t t; - - s->odd &= 0xffffff; - t = s->odd, s->odd = s->even, s->even = t; - - out = s->even & 1; - out ^= LF_POLY_EVEN & (s->even >>= 1); - out ^= LF_POLY_ODD & s->odd; - out ^= !!in; - out ^= (ret = filter(s->odd)) & !!fb; - - s->even |= parity(out) << 23; - return ret; -} -/** lfsr_rollback_byte - * Rollback the shift register in order to get previous states - */ -uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) -{ - /* - int i, ret = 0; - for (i = 7; i >= 0; --i) - ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i; -*/ -// unfold loop 20160112 - uint8_t ret = 0; - ret |= lfsr_rollback_bit(s, BIT(in, 7), fb) << 7; - ret |= lfsr_rollback_bit(s, BIT(in, 6), fb) << 6; - ret |= lfsr_rollback_bit(s, BIT(in, 5), fb) << 5; - ret |= lfsr_rollback_bit(s, BIT(in, 4), fb) << 4; - ret |= lfsr_rollback_bit(s, BIT(in, 3), fb) << 3; - ret |= lfsr_rollback_bit(s, BIT(in, 2), fb) << 2; - ret |= lfsr_rollback_bit(s, BIT(in, 1), fb) << 1; - ret |= lfsr_rollback_bit(s, BIT(in, 0), fb) << 0; - return ret; -} -/** lfsr_rollback_word - * Rollback the shift register in order to get previous states - */ -uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) -{ - /* - int i; - uint32_t ret = 0; - for (i = 31; i >= 0; --i) - ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24); -*/ -// unfold loop 20160112 - uint32_t ret = 0; - ret |= lfsr_rollback_bit(s, BEBIT(in, 31), fb) << (31 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 30), fb) << (30 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 29), fb) << (29 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 28), fb) << (28 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 27), fb) << (27 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 26), fb) << (26 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 25), fb) << (25 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 24), fb) << (24 ^ 24); - - ret |= lfsr_rollback_bit(s, BEBIT(in, 23), fb) << (23 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 22), fb) << (22 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 21), fb) << (21 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 20), fb) << (20 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 19), fb) << (19 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 18), fb) << (18 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 17), fb) << (17 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 16), fb) << (16 ^ 24); - - ret |= lfsr_rollback_bit(s, BEBIT(in, 15), fb) << (15 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 14), fb) << (14 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 13), fb) << (13 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 12), fb) << (12 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 11), fb) << (11 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 10), fb) << (10 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 9), fb) << (9 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 8), fb) << (8 ^ 24); - - ret |= lfsr_rollback_bit(s, BEBIT(in, 7), fb) << (7 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 6), fb) << (6 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 5), fb) << (5 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 4), fb) << (4 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 3), fb) << (3 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 2), fb) << (2 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 1), fb) << (1 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 0), fb) << (0 ^ 24); - return ret; -} - -/** nonce_distance - * x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y - */ -static uint16_t *dist = 0; -int nonce_distance(uint32_t from, uint32_t to) -{ - uint16_t x, i; - if(!dist) { - dist = malloc(2 << 16); - if(!dist) - return -1; - for (x = i = 1; i; ++i) { - dist[(x & 0xff) << 8 | x >> 8] = i; - x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; - } - } - return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; -} - - -static uint32_t fastfwd[2][8] = { - { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, - { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; - - -/** lfsr_prefix_ks - * - * Is an exported helper function from the common prefix attack - * Described in the "dark side" paper. It returns an -1 terminated array - * of possible partial(21 bit) secret state. - * The required keystream(ks) needs to contain the keystream that was used to - * encrypt the NACK which is observed when varying only the 3 last bits of Nr - * only correct iff [NR_3] ^ NR_3 does not depend on Nr_3 - */ -uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) -{ - uint32_t *candidates = malloc(4 << 10); - if(!candidates) return 0; - - uint32_t c, entry; - int size = 0, i, good; - - for(i = 0; i < 1 << 21; ++i) { - for(c = 0, good = 1; good && c < 8; ++c) { - entry = i ^ fastfwd[isodd][c]; - good &= (BIT(ks[c], isodd) == filter(entry >> 1)); - good &= (BIT(ks[c], isodd + 2) == filter(entry)); - } - if(good) - candidates[size++] = i; - } - - candidates[size] = -1; - - return candidates; -} - -/** check_pfx_parity - * helper function which eliminates possible secret states using parity bits - */ -static struct Crypto1State* check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], uint32_t odd, uint32_t even, struct Crypto1State* sl) -{ - uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; - - for(c = 0; good && c < 8; ++c) { - sl->odd = odd ^ fastfwd[1][c]; - sl->even = even ^ fastfwd[0][c]; - - lfsr_rollback_bit(sl, 0, 0); - lfsr_rollback_bit(sl, 0, 0); - - ks3 = lfsr_rollback_bit(sl, 0, 0); - ks2 = lfsr_rollback_word(sl, 0, 0); - ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1); - - nr = ks1 ^ (prefix | c << 5); - rr = ks2 ^ rresp; - - good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); - good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); - good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); - good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); - good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3; - } - - return sl + good; -} - -/** lfsr_common_prefix - * Implentation of the common prefix attack. - * Requires the 28 bit constant prefix used as reader nonce (pfx) - * The reader response used (rr) - * The keystream used to encrypt the observed NACK's (ks) - * The parity bits (par) - * It returns a zero terminated list of possible cipher states after the - * tag nonce was fed in - */ - -struct Crypto1State* lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) -{ - struct Crypto1State *statelist, *s; - uint32_t *odd, *even, *o, *e, top; - - odd = lfsr_prefix_ks(ks, 1); - even = lfsr_prefix_ks(ks, 0); - - s = statelist = malloc((sizeof *statelist) << 24); - if(!s || !odd || !even) { - free(statelist); - statelist = 0; - goto out; - } - - for(o = odd; *o + 1; ++o) - for(e = even; *e + 1; ++e) - for(top = 0; top < 64; ++top) { - *o += 1 << 21; - *e += (!(top & 7) + 1) << 21; - s = check_pfx_parity(pfx, rr, par, *o, *e, s); - } - - s->odd = s->even = 0; -out: - free(odd); - free(even); - return statelist; -} +/* crapto1.c + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, US$ + + Copyright (C) 2008-2014 bla +*/ +#include "crapto1.h" +#include + +#if !defined LOWMEM && defined __GNUC__ +static uint8_t filterlut[1 << 20]; +static void __attribute__((constructor)) fill_lut() +{ + uint32_t i; + for(i = 0; i < 1 << 20; ++i) + filterlut[i] = filter(i); +} +#define filter(x) (filterlut[(x) & 0xfffff]) +#endif + + + +typedef struct bucket { + uint32_t *head; + uint32_t *bp; +} bucket_t; + +typedef bucket_t bucket_array_t[2][0x100]; + +typedef struct bucket_info { + struct { + uint32_t *head, *tail; + } bucket_info[2][0x100]; + uint32_t numbuckets; + } bucket_info_t; + + +static void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, + uint32_t* const ostart, uint32_t* const ostop, + bucket_info_t *bucket_info, bucket_array_t bucket) +{ + uint32_t *p1, *p2; + uint32_t *start[2]; + uint32_t *stop[2]; + + start[0] = estart; + stop[0] = estop; + start[1] = ostart; + stop[1] = ostop; + + // init buckets to be empty + for (uint32_t i = 0; i < 2; i++) { + for (uint32_t j = 0x00; j <= 0xff; j++) { + bucket[i][j].bp = bucket[i][j].head; + } + } + + // sort the lists into the buckets based on the MSB (contribution bits) + for (uint32_t i = 0; i < 2; i++) { + for (p1 = start[i]; p1 <= stop[i]; p1++) { + uint32_t bucket_index = (*p1 & 0xff000000) >> 24; + *(bucket[i][bucket_index].bp++) = *p1; + } + } + + + // write back intersecting buckets as sorted list. + // fill in bucket_info with head and tail of the bucket contents in the list and number of non-empty buckets. + uint32_t nonempty_bucket; + for (uint32_t i = 0; i < 2; i++) { + p1 = start[i]; + nonempty_bucket = 0; + for (uint32_t j = 0x00; j <= 0xff; j++) { + if (bucket[0][j].bp != bucket[0][j].head && bucket[1][j].bp != bucket[1][j].head) { // non-empty intersecting buckets only + bucket_info->bucket_info[i][nonempty_bucket].head = p1; + for (p2 = bucket[i][j].head; p2 < bucket[i][j].bp; *p1++ = *p2++); + bucket_info->bucket_info[i][nonempty_bucket].tail = p1 - 1; + nonempty_bucket++; + } + } + bucket_info->numbuckets = nonempty_bucket; + } +} + + +/** update_contribution + * helper, calculates the partial linear feedback contributions and puts in MSB + */ +static inline void update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) +{ + uint32_t p = *item >> 25; + + p = p << 1 | parity(*item & mask1); + p = p << 1 | parity(*item & mask2); + *item = p << 24 | (*item & 0xffffff); +} + +/** extend_table + * using a bit of the keystream extend the table of possible lfsr states + */ +static inline void extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) +{ + in <<= 24; + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if(filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else if(filter(*tbl) == bit) { + *++*end = tbl[1]; + tbl[1] = tbl[0] | 1; + update_contribution(tbl, m1, m2); + *tbl++ ^= in; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else + *tbl-- = *(*end)--; +} +/** extend_table_simple + * using a bit of the keystream extend the table of possible lfsr states + */ +static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) +{ + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) { + if(filter(*tbl) ^ filter(*tbl | 1)) { // replace + *tbl |= filter(*tbl) ^ bit; + } else if(filter(*tbl) == bit) { // insert + *++*end = *++tbl; + *tbl = tbl[-1] | 1; + } else { // drop + *tbl-- = *(*end)--; + } + } +} +/** recover + * recursively narrow down the search space, 4 bits of keystream at a time + */ +static struct Crypto1State* +recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, + uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, + struct Crypto1State *sl, uint32_t in, bucket_array_t bucket) +{ + uint32_t *o, *e; + bucket_info_t bucket_info; + + if(rem == -1) { + for(e = e_head; e <= e_tail; ++e) { + *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); + for(o = o_head; o <= o_tail; ++o, ++sl) { + sl->even = *o; + sl->odd = *e ^ parity(*o & LF_POLY_ODD); + sl[1].odd = sl[1].even = 0; + } + } + return sl; + } + + for(uint32_t i = 0; i < 4 && rem--; i++) { + oks >>= 1; + eks >>= 1; + in >>= 2; + extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0); + if(o_head > o_tail) + return sl; + + extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3); + if(e_head > e_tail) + return sl; + } + + bucket_sort_intersect(e_head, e_tail, o_head, o_tail, &bucket_info, bucket); + + for (int i = bucket_info.numbuckets - 1; i >= 0; i--) { + sl = recover(bucket_info.bucket_info[1][i].head, bucket_info.bucket_info[1][i].tail, oks, + bucket_info.bucket_info[0][i].head, bucket_info.bucket_info[0][i].tail, eks, + rem, sl, in, bucket); + } + + return sl; +} +/** lfsr_recovery + * recover the state of the lfsr given 32 bits of the keystream + * additionally you can use the in parameter to specify the value + * that was fed into the lfsr at the time the keystream was generated + */ +struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) +{ + struct Crypto1State *statelist; + uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; + uint32_t *even_head = 0, *even_tail = 0, eks = 0; + int i; + + // split the keystream into an odd and even part + for(i = 31; i >= 0; i -= 2) + oks = oks << 1 | BEBIT(ks2, i); + for(i = 30; i >= 0; i -= 2) + eks = eks << 1 | BEBIT(ks2, i); + + odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); + even_head = even_tail = malloc(sizeof(uint32_t) << 21); + statelist = malloc(sizeof(struct Crypto1State) << 18); + if(!odd_tail-- || !even_tail-- || !statelist) { + free(statelist); + statelist = 0; + goto out; + } + + statelist->odd = statelist->even = 0; + + // allocate memory for out of place bucket_sort + bucket_array_t bucket; + + for (uint32_t i = 0; i < 2; i++) { + for (uint32_t j = 0; j <= 0xff; j++) { + bucket[i][j].head = malloc(sizeof(uint32_t)<<14); + if (!bucket[i][j].head) { + goto out; + } + } + } + + // initialize statelists: add all possible states which would result into the rightmost 2 bits of the keystream + for(i = 1 << 20; i >= 0; --i) { + if(filter(i) == (oks & 1)) + *++odd_tail = i; + if(filter(i) == (eks & 1)) + *++even_tail = i; + } + + // extend the statelists. Look at the next 8 Bits of the keystream (4 Bit each odd and even): + for(i = 0; i < 4; i++) { + extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); + extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); + } + + // the statelists now contain all states which could have generated the last 10 Bits of the keystream. + // 22 bits to go to recover 32 bits in total. From now on, we need to take the "in" + // parameter into account. + in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); // Byte swapping + recover(odd_head, odd_tail, oks, even_head, even_tail, eks, 11, statelist, in << 1, bucket); + +out: + for (uint32_t i = 0; i < 2; i++) + for (uint32_t j = 0; j <= 0xff; j++) + free(bucket[i][j].head); + free(odd_head); + free(even_head); + return statelist; +} + +static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214, + 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, + 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA}; +static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60, + 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, + 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, + 0x7EC7EE90, 0x7F63F748, 0x79117020}; +static const uint32_t T1[] = { + 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, + 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, + 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, + 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C}; +static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0, + 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, + 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, + 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, + 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, + 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0}; +static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD}; +static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0}; +/** Reverse 64 bits of keystream into possible cipher states + * Variation mentioned in the paper. Somewhat optimized version + */ +struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) +{ + struct Crypto1State *statelist, *sl; + uint8_t oks[32], eks[32], hi[32]; + uint32_t low = 0, win = 0; + uint32_t *tail, table[1 << 16]; + int i, j; + + sl = statelist = malloc(sizeof(struct Crypto1State) << 4); + if(!sl) + return 0; + sl->odd = sl->even = 0; + + for(i = 30; i >= 0; i -= 2) { + oks[i >> 1] = BEBIT(ks2, i); + oks[16 + (i >> 1)] = BEBIT(ks3, i); + } + for(i = 31; i >= 0; i -= 2) { + eks[i >> 1] = BEBIT(ks2, i); + eks[16 + (i >> 1)] = BEBIT(ks3, i); + } + + for(i = 0xfffff; i >= 0; --i) { + if (filter(i) != oks[0]) + continue; + + *(tail = table) = i; + for(j = 1; tail >= table && j < 29; ++j) + extend_table_simple(table, &tail, oks[j]); + + if(tail < table) + continue; + + for(j = 0; j < 19; ++j) + low = low << 1 | parity(i & S1[j]); + for(j = 0; j < 32; ++j) + hi[j] = parity(i & T1[j]); + + for(; tail >= table; --tail) { + for(j = 0; j < 3; ++j) { + *tail = *tail << 1; + *tail |= parity((i & C1[j]) ^ (*tail & C2[j])); + if(filter(*tail) != oks[29 + j]) + goto continue2; + } + + for(j = 0; j < 19; ++j) + win = win << 1 | parity(*tail & S2[j]); + + win ^= low; + for(j = 0; j < 32; ++j) { + win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); + if(filter(win) != eks[j]) + goto continue2; + } + + *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); + sl->odd = *tail ^ parity(LF_POLY_ODD & win); + sl->even = win; + ++sl; + sl->odd = sl->even = 0; + continue2:; + } + } + return statelist; +} + +/** lfsr_rollback_bit + * Rollback the shift register in order to get previous states + */ +uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) +{ + int out; + uint8_t ret; + uint32_t t; + + s->odd &= 0xffffff; + t = s->odd, s->odd = s->even, s->even = t; + + out = s->even & 1; + out ^= LF_POLY_EVEN & (s->even >>= 1); + out ^= LF_POLY_ODD & s->odd; + out ^= !!in; + out ^= (ret = filter(s->odd)) & !!fb; + + s->even |= parity(out) << 23; + return ret; +} +/** lfsr_rollback_byte + * Rollback the shift register in order to get previous states + */ +uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) +{ + /* + int i, ret = 0; + for (i = 7; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i; +*/ +// unfold loop 20160112 + uint8_t ret = 0; + ret |= lfsr_rollback_bit(s, BIT(in, 7), fb) << 7; + ret |= lfsr_rollback_bit(s, BIT(in, 6), fb) << 6; + ret |= lfsr_rollback_bit(s, BIT(in, 5), fb) << 5; + ret |= lfsr_rollback_bit(s, BIT(in, 4), fb) << 4; + ret |= lfsr_rollback_bit(s, BIT(in, 3), fb) << 3; + ret |= lfsr_rollback_bit(s, BIT(in, 2), fb) << 2; + ret |= lfsr_rollback_bit(s, BIT(in, 1), fb) << 1; + ret |= lfsr_rollback_bit(s, BIT(in, 0), fb) << 0; + return ret; +} +/** lfsr_rollback_word + * Rollback the shift register in order to get previous states + */ +uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) +{ + /* + int i; + uint32_t ret = 0; + for (i = 31; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24); +*/ +// unfold loop 20160112 + uint32_t ret = 0; + ret |= lfsr_rollback_bit(s, BEBIT(in, 31), fb) << (31 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 30), fb) << (30 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 29), fb) << (29 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 28), fb) << (28 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 27), fb) << (27 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 26), fb) << (26 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 25), fb) << (25 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 24), fb) << (24 ^ 24); + + ret |= lfsr_rollback_bit(s, BEBIT(in, 23), fb) << (23 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 22), fb) << (22 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 21), fb) << (21 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 20), fb) << (20 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 19), fb) << (19 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 18), fb) << (18 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 17), fb) << (17 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 16), fb) << (16 ^ 24); + + ret |= lfsr_rollback_bit(s, BEBIT(in, 15), fb) << (15 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 14), fb) << (14 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 13), fb) << (13 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 12), fb) << (12 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 11), fb) << (11 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 10), fb) << (10 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 9), fb) << (9 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 8), fb) << (8 ^ 24); + + ret |= lfsr_rollback_bit(s, BEBIT(in, 7), fb) << (7 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 6), fb) << (6 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 5), fb) << (5 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 4), fb) << (4 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 3), fb) << (3 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 2), fb) << (2 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 1), fb) << (1 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 0), fb) << (0 ^ 24); + return ret; +} + +/** nonce_distance + * x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y + */ +static uint16_t *dist = 0; +int nonce_distance(uint32_t from, uint32_t to) +{ + uint16_t x, i; + if(!dist) { + dist = malloc(2 << 16); + if(!dist) + return -1; + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + } + return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; +} + + +static uint32_t fastfwd[2][8] = { + { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, + { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; + + +/** lfsr_prefix_ks + * + * Is an exported helper function from the common prefix attack + * Described in the "dark side" paper. It returns an -1 terminated array + * of possible partial(21 bit) secret state. + * The required keystream(ks) needs to contain the keystream that was used to + * encrypt the NACK which is observed when varying only the 3 last bits of Nr + * only correct iff [NR_3] ^ NR_3 does not depend on Nr_3 + */ +uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) +{ + uint32_t *candidates = malloc(4 << 10); + if(!candidates) return 0; + + uint32_t c, entry; + int size = 0, i, good; + + for(i = 0; i < 1 << 21; ++i) { + for(c = 0, good = 1; good && c < 8; ++c) { + entry = i ^ fastfwd[isodd][c]; + good &= (BIT(ks[c], isodd) == filter(entry >> 1)); + good &= (BIT(ks[c], isodd + 2) == filter(entry)); + } + if(good) + candidates[size++] = i; + } + + candidates[size] = -1; + + return candidates; +} + +/** check_pfx_parity + * helper function which eliminates possible secret states using parity bits + */ +static struct Crypto1State* check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], uint32_t odd, uint32_t even, struct Crypto1State* sl) +{ + uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; + + for(c = 0; good && c < 8; ++c) { + sl->odd = odd ^ fastfwd[1][c]; + sl->even = even ^ fastfwd[0][c]; + + lfsr_rollback_bit(sl, 0, 0); + lfsr_rollback_bit(sl, 0, 0); + + ks3 = lfsr_rollback_bit(sl, 0, 0); + ks2 = lfsr_rollback_word(sl, 0, 0); + ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1); + + nr = ks1 ^ (prefix | c << 5); + rr = ks2 ^ rresp; + + good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); + good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); + good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); + good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); + good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3; + } + + return sl + good; +} + +/** lfsr_common_prefix + * Implentation of the common prefix attack. + * Requires the 28 bit constant prefix used as reader nonce (pfx) + * The reader response used (rr) + * The keystream used to encrypt the observed NACK's (ks) + * The parity bits (par) + * It returns a zero terminated list of possible cipher states after the + * tag nonce was fed in + */ + +struct Crypto1State* lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) +{ + struct Crypto1State *statelist, *s; + uint32_t *odd, *even, *o, *e, top; + + odd = lfsr_prefix_ks(ks, 1); + even = lfsr_prefix_ks(ks, 0); + + s = statelist = malloc((sizeof *statelist) << 24); + if(!s || !odd || !even) { + free(statelist); + statelist = 0; + goto out; + } + + for(o = odd; *o + 1; ++o) + for(e = even; *e + 1; ++e) + for(top = 0; top < 64; ++top) { + *o += 1 << 21; + *e += (!(top & 7) + 1) << 21; + s = check_pfx_parity(pfx, rr, par, *o, *e, s); + } + + s->odd = s->even = 0; +out: + free(odd); + free(even); + return statelist; +} diff --git a/tools/nonce2key/crapto1.c b/tools/nonce2key/crapto1.c index 1d854d96b..fcfa931c4 100644 --- a/tools/nonce2key/crapto1.c +++ b/tools/nonce2key/crapto1.c @@ -1,573 +1,573 @@ -/* crapto1.c - - This program is free software; you can redistribute it and/or - modify it under the terms of the GNU General Public License - as published by the Free Software Foundation; either version 2 - of the License, or (at your option) any later version. - - This program is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with this program; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, - Boston, MA 02110-1301, US$ - - Copyright (C) 2008-2014 bla -*/ -#include "crapto1.h" -#include - -#if !defined LOWMEM && defined __GNUC__ -static uint8_t filterlut[1 << 20]; -static void __attribute__((constructor)) fill_lut() -{ - uint32_t i; - for(i = 0; i < 1 << 20; ++i) - filterlut[i] = filter(i); -} -#define filter(x) (filterlut[(x) & 0xfffff]) -#endif - - - -typedef struct bucket { - uint32_t *head; - uint32_t *bp; -} bucket_t; - -typedef bucket_t bucket_array_t[2][0x100]; - -typedef struct bucket_info { - struct { - uint32_t *head, *tail; - } bucket_info[2][0x100]; - uint32_t numbuckets; - } bucket_info_t; - - -static void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, - uint32_t* const ostart, uint32_t* const ostop, - bucket_info_t *bucket_info, bucket_array_t bucket) -{ - uint32_t *p1, *p2; - uint32_t *start[2]; - uint32_t *stop[2]; - - start[0] = estart; - stop[0] = estop; - start[1] = ostart; - stop[1] = ostop; - - // init buckets to be empty - for (uint32_t i = 0; i < 2; i++) { - for (uint32_t j = 0x00; j <= 0xff; j++) { - bucket[i][j].bp = bucket[i][j].head; - } - } - - // sort the lists into the buckets based on the MSB (contribution bits) - for (uint32_t i = 0; i < 2; i++) { - for (p1 = start[i]; p1 <= stop[i]; p1++) { - uint32_t bucket_index = (*p1 & 0xff000000) >> 24; - *(bucket[i][bucket_index].bp++) = *p1; - } - } - - - // write back intersecting buckets as sorted list. - // fill in bucket_info with head and tail of the bucket contents in the list and number of non-empty buckets. - uint32_t nonempty_bucket; - for (uint32_t i = 0; i < 2; i++) { - p1 = start[i]; - nonempty_bucket = 0; - for (uint32_t j = 0x00; j <= 0xff; j++) { - if (bucket[0][j].bp != bucket[0][j].head && bucket[1][j].bp != bucket[1][j].head) { // non-empty intersecting buckets only - bucket_info->bucket_info[i][nonempty_bucket].head = p1; - for (p2 = bucket[i][j].head; p2 < bucket[i][j].bp; *p1++ = *p2++); - bucket_info->bucket_info[i][nonempty_bucket].tail = p1 - 1; - nonempty_bucket++; - } - } - bucket_info->numbuckets = nonempty_bucket; - } -} - -/** update_contribution - * helper, calculates the partial linear feedback contributions and puts in MSB - */ -static inline void update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) -{ - uint32_t p = *item >> 25; - - p = p << 1 | parity(*item & mask1); - p = p << 1 | parity(*item & mask2); - *item = p << 24 | (*item & 0xffffff); -} - -/** extend_table - * using a bit of the keystream extend the table of possible lfsr states - */ -static inline void extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) -{ - in <<= 24; - for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) - if(filter(*tbl) ^ filter(*tbl | 1)) { - *tbl |= filter(*tbl) ^ bit; - update_contribution(tbl, m1, m2); - *tbl ^= in; - } else if(filter(*tbl) == bit) { - *++*end = tbl[1]; - tbl[1] = tbl[0] | 1; - update_contribution(tbl, m1, m2); - *tbl++ ^= in; - update_contribution(tbl, m1, m2); - *tbl ^= in; - } else - *tbl-- = *(*end)--; -} -/** extend_table_simple - * using a bit of the keystream extend the table of possible lfsr states - */ -static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) -{ - for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) { - if(filter(*tbl) ^ filter(*tbl | 1)) { // replace - *tbl |= filter(*tbl) ^ bit; - } else if(filter(*tbl) == bit) { // insert - *++*end = *++tbl; - *tbl = tbl[-1] | 1; - } else { // drop - *tbl-- = *(*end)--; - } - } -} -/** recover - * recursively narrow down the search space, 4 bits of keystream at a time - */ -static struct Crypto1State* -recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, - uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, - struct Crypto1State *sl, uint32_t in, bucket_array_t bucket) -{ - uint32_t *o, *e; - bucket_info_t bucket_info; - - if(rem == -1) { - for(e = e_head; e <= e_tail; ++e) { - *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); - for(o = o_head; o <= o_tail; ++o, ++sl) { - sl->even = *o; - sl->odd = *e ^ parity(*o & LF_POLY_ODD); - sl[1].odd = sl[1].even = 0; - } - } - return sl; - } - - for(uint32_t i = 0; i < 4 && rem--; i++) { - oks >>= 1; - eks >>= 1; - in >>= 2; - extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0); - if(o_head > o_tail) - return sl; - - extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3); - if(e_head > e_tail) - return sl; - } - - bucket_sort_intersect(e_head, e_tail, o_head, o_tail, &bucket_info, bucket); - - for (int i = bucket_info.numbuckets - 1; i >= 0; i--) { - sl = recover(bucket_info.bucket_info[1][i].head, bucket_info.bucket_info[1][i].tail, oks, - bucket_info.bucket_info[0][i].head, bucket_info.bucket_info[0][i].tail, eks, - rem, sl, in, bucket); - } - - return sl; -} -/** lfsr_recovery - * recover the state of the lfsr given 32 bits of the keystream - * additionally you can use the in parameter to specify the value - * that was fed into the lfsr at the time the keystream was generated - */ -struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) -{ - struct Crypto1State *statelist; - uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; - uint32_t *even_head = 0, *even_tail = 0, eks = 0; - int i; - - // split the keystream into an odd and even part - for(i = 31; i >= 0; i -= 2) - oks = oks << 1 | BEBIT(ks2, i); - for(i = 30; i >= 0; i -= 2) - eks = eks << 1 | BEBIT(ks2, i); - - odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); - even_head = even_tail = malloc(sizeof(uint32_t) << 21); - statelist = malloc(sizeof(struct Crypto1State) << 18); - if(!odd_tail-- || !even_tail-- || !statelist) { - free(statelist); - statelist = 0; - goto out; - } - - statelist->odd = statelist->even = 0; - - // allocate memory for out of place bucket_sort - bucket_array_t bucket; - - for (uint32_t i = 0; i < 2; i++) { - for (uint32_t j = 0; j <= 0xff; j++) { - bucket[i][j].head = malloc(sizeof(uint32_t)<<14); - if (!bucket[i][j].head) { - goto out; - } - } - } - - // initialize statelists: add all possible states which would result into the rightmost 2 bits of the keystream - for(i = 1 << 20; i >= 0; --i) { - if(filter(i) == (oks & 1)) - *++odd_tail = i; - if(filter(i) == (eks & 1)) - *++even_tail = i; - } - - // extend the statelists. Look at the next 8 Bits of the keystream (4 Bit each odd and even): - for(i = 0; i < 4; i++) { - extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); - extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); - } - - // the statelists now contain all states which could have generated the last 10 Bits of the keystream. - // 22 bits to go to recover 32 bits in total. From now on, we need to take the "in" - // parameter into account. - in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); // Byte swapping - recover(odd_head, odd_tail, oks, even_head, even_tail, eks, 11, statelist, in << 1, bucket); - -out: - for (uint32_t i = 0; i < 2; i++) - for (uint32_t j = 0; j <= 0xff; j++) - free(bucket[i][j].head); - free(odd_head); - free(even_head); - return statelist; -} - -static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214, - 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, - 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA}; -static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60, - 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, - 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, - 0x7EC7EE90, 0x7F63F748, 0x79117020}; -static const uint32_t T1[] = { - 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, - 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, - 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, - 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C}; -static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0, - 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, - 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, - 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, - 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, - 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0}; -static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD}; -static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0}; -/** Reverse 64 bits of keystream into possible cipher states - * Variation mentioned in the paper. Somewhat optimized version - */ -struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) -{ - struct Crypto1State *statelist, *sl; - uint8_t oks[32], eks[32], hi[32]; - uint32_t low = 0, win = 0; - uint32_t *tail, table[1 << 16]; - int i, j; - - sl = statelist = malloc(sizeof(struct Crypto1State) << 4); - if(!sl) - return 0; - sl->odd = sl->even = 0; - - for(i = 30; i >= 0; i -= 2) { - oks[i >> 1] = BEBIT(ks2, i); - oks[16 + (i >> 1)] = BEBIT(ks3, i); - } - for(i = 31; i >= 0; i -= 2) { - eks[i >> 1] = BEBIT(ks2, i); - eks[16 + (i >> 1)] = BEBIT(ks3, i); - } - - for(i = 0xfffff; i >= 0; --i) { - if (filter(i) != oks[0]) - continue; - - *(tail = table) = i; - for(j = 1; tail >= table && j < 29; ++j) - extend_table_simple(table, &tail, oks[j]); - - if(tail < table) - continue; - - for(j = 0; j < 19; ++j) - low = low << 1 | parity(i & S1[j]); - for(j = 0; j < 32; ++j) - hi[j] = parity(i & T1[j]); - - for(; tail >= table; --tail) { - for(j = 0; j < 3; ++j) { - *tail = *tail << 1; - *tail |= parity((i & C1[j]) ^ (*tail & C2[j])); - if(filter(*tail) != oks[29 + j]) - goto continue2; - } - - for(j = 0; j < 19; ++j) - win = win << 1 | parity(*tail & S2[j]); - - win ^= low; - for(j = 0; j < 32; ++j) { - win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); - if(filter(win) != eks[j]) - goto continue2; - } - - *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); - sl->odd = *tail ^ parity(LF_POLY_ODD & win); - sl->even = win; - ++sl; - sl->odd = sl->even = 0; - continue2:; - } - } - return statelist; -} - -/** lfsr_rollback_bit - * Rollback the shift register in order to get previous states - */ -uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) -{ - int out; - uint8_t ret; - uint32_t t; - - s->odd &= 0xffffff; - t = s->odd, s->odd = s->even, s->even = t; - - out = s->even & 1; - out ^= LF_POLY_EVEN & (s->even >>= 1); - out ^= LF_POLY_ODD & s->odd; - out ^= !!in; - out ^= (ret = filter(s->odd)) & !!fb; - - s->even |= parity(out) << 23; - return ret; -} -/** lfsr_rollback_byte - * Rollback the shift register in order to get previous states - */ -uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) -{ - /* - int i, ret = 0; - for (i = 7; i >= 0; --i) - ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i; -*/ -// unfold loop 20160112 - uint8_t ret = 0; - ret |= lfsr_rollback_bit(s, BIT(in, 7), fb) << 7; - ret |= lfsr_rollback_bit(s, BIT(in, 6), fb) << 6; - ret |= lfsr_rollback_bit(s, BIT(in, 5), fb) << 5; - ret |= lfsr_rollback_bit(s, BIT(in, 4), fb) << 4; - ret |= lfsr_rollback_bit(s, BIT(in, 3), fb) << 3; - ret |= lfsr_rollback_bit(s, BIT(in, 2), fb) << 2; - ret |= lfsr_rollback_bit(s, BIT(in, 1), fb) << 1; - ret |= lfsr_rollback_bit(s, BIT(in, 0), fb) << 0; - return ret; -} -/** lfsr_rollback_word - * Rollback the shift register in order to get previous states - */ -uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) -{ - /* - int i; - uint32_t ret = 0; - for (i = 31; i >= 0; --i) - ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24); -*/ -// unfold loop 20160112 - uint32_t ret = 0; - ret |= lfsr_rollback_bit(s, BEBIT(in, 31), fb) << (31 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 30), fb) << (30 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 29), fb) << (29 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 28), fb) << (28 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 27), fb) << (27 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 26), fb) << (26 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 25), fb) << (25 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 24), fb) << (24 ^ 24); - - ret |= lfsr_rollback_bit(s, BEBIT(in, 23), fb) << (23 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 22), fb) << (22 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 21), fb) << (21 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 20), fb) << (20 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 19), fb) << (19 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 18), fb) << (18 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 17), fb) << (17 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 16), fb) << (16 ^ 24); - - ret |= lfsr_rollback_bit(s, BEBIT(in, 15), fb) << (15 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 14), fb) << (14 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 13), fb) << (13 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 12), fb) << (12 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 11), fb) << (11 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 10), fb) << (10 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 9), fb) << (9 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 8), fb) << (8 ^ 24); - - ret |= lfsr_rollback_bit(s, BEBIT(in, 7), fb) << (7 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 6), fb) << (6 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 5), fb) << (5 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 4), fb) << (4 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 3), fb) << (3 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 2), fb) << (2 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 1), fb) << (1 ^ 24); - ret |= lfsr_rollback_bit(s, BEBIT(in, 0), fb) << (0 ^ 24); - return ret; -} - -/** nonce_distance - * x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y - */ -static uint16_t *dist = 0; -int nonce_distance(uint32_t from, uint32_t to) -{ - uint16_t x, i; - if(!dist) { - dist = malloc(2 << 16); - if(!dist) - return -1; - for (x = i = 1; i; ++i) { - dist[(x & 0xff) << 8 | x >> 8] = i; - x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; - } - } - return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; -} - - -static uint32_t fastfwd[2][8] = { - { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, - { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; - - -/** lfsr_prefix_ks - * - * Is an exported helper function from the common prefix attack - * Described in the "dark side" paper. It returns an -1 terminated array - * of possible partial(21 bit) secret state. - * The required keystream(ks) needs to contain the keystream that was used to - * encrypt the NACK which is observed when varying only the 3 last bits of Nr - * only correct iff [NR_3] ^ NR_3 does not depend on Nr_3 - */ -uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) -{ - uint32_t *candidates = malloc(4 << 10); - if(!candidates) return 0; - - uint32_t c, entry; - int size = 0, i, good; - - for(i = 0; i < 1 << 21; ++i) { - for(c = 0, good = 1; good && c < 8; ++c) { - entry = i ^ fastfwd[isodd][c]; - good &= (BIT(ks[c], isodd) == filter(entry >> 1)); - good &= (BIT(ks[c], isodd + 2) == filter(entry)); - } - if(good) - candidates[size++] = i; - } - - candidates[size] = -1; - - return candidates; -} - -/** check_pfx_parity - * helper function which eliminates possible secret states using parity bits - */ -static struct Crypto1State* check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], uint32_t odd, uint32_t even, struct Crypto1State* sl) -{ - uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; - - for(c = 0; good && c < 8; ++c) { - sl->odd = odd ^ fastfwd[1][c]; - sl->even = even ^ fastfwd[0][c]; - - lfsr_rollback_bit(sl, 0, 0); - lfsr_rollback_bit(sl, 0, 0); - - ks3 = lfsr_rollback_bit(sl, 0, 0); - ks2 = lfsr_rollback_word(sl, 0, 0); - ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1); - - nr = ks1 ^ (prefix | c << 5); - rr = ks2 ^ rresp; - - good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); - good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); - good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); - good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); - good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3; - } - - return sl + good; -} - -/** lfsr_common_prefix - * Implentation of the common prefix attack. - * Requires the 28 bit constant prefix used as reader nonce (pfx) - * The reader response used (rr) - * The keystream used to encrypt the observed NACK's (ks) - * The parity bits (par) - * It returns a zero terminated list of possible cipher states after the - * tag nonce was fed in - */ - -struct Crypto1State* lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) -{ - struct Crypto1State *statelist, *s; - uint32_t *odd, *even, *o, *e, top; - - odd = lfsr_prefix_ks(ks, 1); - even = lfsr_prefix_ks(ks, 0); - - s = statelist = malloc((sizeof *statelist) << 20); - if(!s || !odd || !even) { - free(statelist); - statelist = 0; - goto out; - } - - for(o = odd; *o + 1; ++o) - for(e = even; *e + 1; ++e) - for(top = 0; top < 64; ++top) { - *o += 1 << 21; - *e += (!(top & 7) + 1) << 21; - s = check_pfx_parity(pfx, rr, par, *o, *e, s); - } - - s->odd = s->even = 0; -out: - free(odd); - free(even); - return statelist; -} +/* crapto1.c + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License + as published by the Free Software Foundation; either version 2 + of the License, or (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, + Boston, MA 02110-1301, US$ + + Copyright (C) 2008-2014 bla +*/ +#include "crapto1.h" +#include + +#if !defined LOWMEM && defined __GNUC__ +static uint8_t filterlut[1 << 20]; +static void __attribute__((constructor)) fill_lut() +{ + uint32_t i; + for(i = 0; i < 1 << 20; ++i) + filterlut[i] = filter(i); +} +#define filter(x) (filterlut[(x) & 0xfffff]) +#endif + + + +typedef struct bucket { + uint32_t *head; + uint32_t *bp; +} bucket_t; + +typedef bucket_t bucket_array_t[2][0x100]; + +typedef struct bucket_info { + struct { + uint32_t *head, *tail; + } bucket_info[2][0x100]; + uint32_t numbuckets; + } bucket_info_t; + + +static void bucket_sort_intersect(uint32_t* const estart, uint32_t* const estop, + uint32_t* const ostart, uint32_t* const ostop, + bucket_info_t *bucket_info, bucket_array_t bucket) +{ + uint32_t *p1, *p2; + uint32_t *start[2]; + uint32_t *stop[2]; + + start[0] = estart; + stop[0] = estop; + start[1] = ostart; + stop[1] = ostop; + + // init buckets to be empty + for (uint32_t i = 0; i < 2; i++) { + for (uint32_t j = 0x00; j <= 0xff; j++) { + bucket[i][j].bp = bucket[i][j].head; + } + } + + // sort the lists into the buckets based on the MSB (contribution bits) + for (uint32_t i = 0; i < 2; i++) { + for (p1 = start[i]; p1 <= stop[i]; p1++) { + uint32_t bucket_index = (*p1 & 0xff000000) >> 24; + *(bucket[i][bucket_index].bp++) = *p1; + } + } + + + // write back intersecting buckets as sorted list. + // fill in bucket_info with head and tail of the bucket contents in the list and number of non-empty buckets. + uint32_t nonempty_bucket; + for (uint32_t i = 0; i < 2; i++) { + p1 = start[i]; + nonempty_bucket = 0; + for (uint32_t j = 0x00; j <= 0xff; j++) { + if (bucket[0][j].bp != bucket[0][j].head && bucket[1][j].bp != bucket[1][j].head) { // non-empty intersecting buckets only + bucket_info->bucket_info[i][nonempty_bucket].head = p1; + for (p2 = bucket[i][j].head; p2 < bucket[i][j].bp; *p1++ = *p2++); + bucket_info->bucket_info[i][nonempty_bucket].tail = p1 - 1; + nonempty_bucket++; + } + } + bucket_info->numbuckets = nonempty_bucket; + } +} + +/** update_contribution + * helper, calculates the partial linear feedback contributions and puts in MSB + */ +static inline void update_contribution(uint32_t *item, const uint32_t mask1, const uint32_t mask2) +{ + uint32_t p = *item >> 25; + + p = p << 1 | parity(*item & mask1); + p = p << 1 | parity(*item & mask2); + *item = p << 24 | (*item & 0xffffff); +} + +/** extend_table + * using a bit of the keystream extend the table of possible lfsr states + */ +static inline void extend_table(uint32_t *tbl, uint32_t **end, int bit, int m1, int m2, uint32_t in) +{ + in <<= 24; + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) + if(filter(*tbl) ^ filter(*tbl | 1)) { + *tbl |= filter(*tbl) ^ bit; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else if(filter(*tbl) == bit) { + *++*end = tbl[1]; + tbl[1] = tbl[0] | 1; + update_contribution(tbl, m1, m2); + *tbl++ ^= in; + update_contribution(tbl, m1, m2); + *tbl ^= in; + } else + *tbl-- = *(*end)--; +} +/** extend_table_simple + * using a bit of the keystream extend the table of possible lfsr states + */ +static inline void extend_table_simple(uint32_t *tbl, uint32_t **end, int bit) +{ + for(*tbl <<= 1; tbl <= *end; *++tbl <<= 1) { + if(filter(*tbl) ^ filter(*tbl | 1)) { // replace + *tbl |= filter(*tbl) ^ bit; + } else if(filter(*tbl) == bit) { // insert + *++*end = *++tbl; + *tbl = tbl[-1] | 1; + } else { // drop + *tbl-- = *(*end)--; + } + } +} +/** recover + * recursively narrow down the search space, 4 bits of keystream at a time + */ +static struct Crypto1State* +recover(uint32_t *o_head, uint32_t *o_tail, uint32_t oks, + uint32_t *e_head, uint32_t *e_tail, uint32_t eks, int rem, + struct Crypto1State *sl, uint32_t in, bucket_array_t bucket) +{ + uint32_t *o, *e; + bucket_info_t bucket_info; + + if(rem == -1) { + for(e = e_head; e <= e_tail; ++e) { + *e = *e << 1 ^ parity(*e & LF_POLY_EVEN) ^ !!(in & 4); + for(o = o_head; o <= o_tail; ++o, ++sl) { + sl->even = *o; + sl->odd = *e ^ parity(*o & LF_POLY_ODD); + sl[1].odd = sl[1].even = 0; + } + } + return sl; + } + + for(uint32_t i = 0; i < 4 && rem--; i++) { + oks >>= 1; + eks >>= 1; + in >>= 2; + extend_table(o_head, &o_tail, oks & 1, LF_POLY_EVEN << 1 | 1, LF_POLY_ODD << 1, 0); + if(o_head > o_tail) + return sl; + + extend_table(e_head, &e_tail, eks & 1, LF_POLY_ODD, LF_POLY_EVEN << 1 | 1, in & 3); + if(e_head > e_tail) + return sl; + } + + bucket_sort_intersect(e_head, e_tail, o_head, o_tail, &bucket_info, bucket); + + for (int i = bucket_info.numbuckets - 1; i >= 0; i--) { + sl = recover(bucket_info.bucket_info[1][i].head, bucket_info.bucket_info[1][i].tail, oks, + bucket_info.bucket_info[0][i].head, bucket_info.bucket_info[0][i].tail, eks, + rem, sl, in, bucket); + } + + return sl; +} +/** lfsr_recovery + * recover the state of the lfsr given 32 bits of the keystream + * additionally you can use the in parameter to specify the value + * that was fed into the lfsr at the time the keystream was generated + */ +struct Crypto1State* lfsr_recovery32(uint32_t ks2, uint32_t in) +{ + struct Crypto1State *statelist; + uint32_t *odd_head = 0, *odd_tail = 0, oks = 0; + uint32_t *even_head = 0, *even_tail = 0, eks = 0; + int i; + + // split the keystream into an odd and even part + for(i = 31; i >= 0; i -= 2) + oks = oks << 1 | BEBIT(ks2, i); + for(i = 30; i >= 0; i -= 2) + eks = eks << 1 | BEBIT(ks2, i); + + odd_head = odd_tail = malloc(sizeof(uint32_t) << 21); + even_head = even_tail = malloc(sizeof(uint32_t) << 21); + statelist = malloc(sizeof(struct Crypto1State) << 18); + if(!odd_tail-- || !even_tail-- || !statelist) { + free(statelist); + statelist = 0; + goto out; + } + + statelist->odd = statelist->even = 0; + + // allocate memory for out of place bucket_sort + bucket_array_t bucket; + + for (uint32_t i = 0; i < 2; i++) { + for (uint32_t j = 0; j <= 0xff; j++) { + bucket[i][j].head = malloc(sizeof(uint32_t)<<14); + if (!bucket[i][j].head) { + goto out; + } + } + } + + // initialize statelists: add all possible states which would result into the rightmost 2 bits of the keystream + for(i = 1 << 20; i >= 0; --i) { + if(filter(i) == (oks & 1)) + *++odd_tail = i; + if(filter(i) == (eks & 1)) + *++even_tail = i; + } + + // extend the statelists. Look at the next 8 Bits of the keystream (4 Bit each odd and even): + for(i = 0; i < 4; i++) { + extend_table_simple(odd_head, &odd_tail, (oks >>= 1) & 1); + extend_table_simple(even_head, &even_tail, (eks >>= 1) & 1); + } + + // the statelists now contain all states which could have generated the last 10 Bits of the keystream. + // 22 bits to go to recover 32 bits in total. From now on, we need to take the "in" + // parameter into account. + in = (in >> 16 & 0xff) | (in << 16) | (in & 0xff00); // Byte swapping + recover(odd_head, odd_tail, oks, even_head, even_tail, eks, 11, statelist, in << 1, bucket); + +out: + for (uint32_t i = 0; i < 2; i++) + for (uint32_t j = 0; j <= 0xff; j++) + free(bucket[i][j].head); + free(odd_head); + free(even_head); + return statelist; +} + +static const uint32_t S1[] = { 0x62141, 0x310A0, 0x18850, 0x0C428, 0x06214, + 0x0310A, 0x85E30, 0xC69AD, 0x634D6, 0xB5CDE, 0xDE8DA, 0x6F46D, 0xB3C83, + 0x59E41, 0xA8995, 0xD027F, 0x6813F, 0x3409F, 0x9E6FA}; +static const uint32_t S2[] = { 0x3A557B00, 0x5D2ABD80, 0x2E955EC0, 0x174AAF60, + 0x0BA557B0, 0x05D2ABD8, 0x0449DE68, 0x048464B0, 0x42423258, 0x278192A8, + 0x156042D0, 0x0AB02168, 0x43F89B30, 0x61FC4D98, 0x765EAD48, 0x7D8FDD20, + 0x7EC7EE90, 0x7F63F748, 0x79117020}; +static const uint32_t T1[] = { + 0x4F37D, 0x279BE, 0x97A6A, 0x4BD35, 0x25E9A, 0x12F4D, 0x097A6, 0x80D66, + 0xC4006, 0x62003, 0xB56B4, 0x5AB5A, 0xA9318, 0xD0F39, 0x6879C, 0xB057B, + 0x582BD, 0x2C15E, 0x160AF, 0x8F6E2, 0xC3DC4, 0xE5857, 0x72C2B, 0x39615, + 0x98DBF, 0xC806A, 0xE0680, 0x70340, 0x381A0, 0x98665, 0x4C332, 0xA272C}; +static const uint32_t T2[] = { 0x3C88B810, 0x5E445C08, 0x2982A580, 0x14C152C0, + 0x4A60A960, 0x253054B0, 0x52982A58, 0x2FEC9EA8, 0x1156C4D0, 0x08AB6268, + 0x42F53AB0, 0x217A9D58, 0x161DC528, 0x0DAE6910, 0x46D73488, 0x25CB11C0, + 0x52E588E0, 0x6972C470, 0x34B96238, 0x5CFC3A98, 0x28DE96C8, 0x12CFC0E0, + 0x4967E070, 0x64B3F038, 0x74F97398, 0x7CDC3248, 0x38CE92A0, 0x1C674950, + 0x0E33A4A8, 0x01B959D0, 0x40DCACE8, 0x26CEDDF0}; +static const uint32_t C1[] = { 0x846B5, 0x4235A, 0x211AD}; +static const uint32_t C2[] = { 0x1A822E0, 0x21A822E0, 0x21A822E0}; +/** Reverse 64 bits of keystream into possible cipher states + * Variation mentioned in the paper. Somewhat optimized version + */ +struct Crypto1State* lfsr_recovery64(uint32_t ks2, uint32_t ks3) +{ + struct Crypto1State *statelist, *sl; + uint8_t oks[32], eks[32], hi[32]; + uint32_t low = 0, win = 0; + uint32_t *tail, table[1 << 16]; + int i, j; + + sl = statelist = malloc(sizeof(struct Crypto1State) << 4); + if(!sl) + return 0; + sl->odd = sl->even = 0; + + for(i = 30; i >= 0; i -= 2) { + oks[i >> 1] = BEBIT(ks2, i); + oks[16 + (i >> 1)] = BEBIT(ks3, i); + } + for(i = 31; i >= 0; i -= 2) { + eks[i >> 1] = BEBIT(ks2, i); + eks[16 + (i >> 1)] = BEBIT(ks3, i); + } + + for(i = 0xfffff; i >= 0; --i) { + if (filter(i) != oks[0]) + continue; + + *(tail = table) = i; + for(j = 1; tail >= table && j < 29; ++j) + extend_table_simple(table, &tail, oks[j]); + + if(tail < table) + continue; + + for(j = 0; j < 19; ++j) + low = low << 1 | parity(i & S1[j]); + for(j = 0; j < 32; ++j) + hi[j] = parity(i & T1[j]); + + for(; tail >= table; --tail) { + for(j = 0; j < 3; ++j) { + *tail = *tail << 1; + *tail |= parity((i & C1[j]) ^ (*tail & C2[j])); + if(filter(*tail) != oks[29 + j]) + goto continue2; + } + + for(j = 0; j < 19; ++j) + win = win << 1 | parity(*tail & S2[j]); + + win ^= low; + for(j = 0; j < 32; ++j) { + win = win << 1 ^ hi[j] ^ parity(*tail & T2[j]); + if(filter(win) != eks[j]) + goto continue2; + } + + *tail = *tail << 1 | parity(LF_POLY_EVEN & *tail); + sl->odd = *tail ^ parity(LF_POLY_ODD & win); + sl->even = win; + ++sl; + sl->odd = sl->even = 0; + continue2:; + } + } + return statelist; +} + +/** lfsr_rollback_bit + * Rollback the shift register in order to get previous states + */ +uint8_t lfsr_rollback_bit(struct Crypto1State *s, uint32_t in, int fb) +{ + int out; + uint8_t ret; + uint32_t t; + + s->odd &= 0xffffff; + t = s->odd, s->odd = s->even, s->even = t; + + out = s->even & 1; + out ^= LF_POLY_EVEN & (s->even >>= 1); + out ^= LF_POLY_ODD & s->odd; + out ^= !!in; + out ^= (ret = filter(s->odd)) & !!fb; + + s->even |= parity(out) << 23; + return ret; +} +/** lfsr_rollback_byte + * Rollback the shift register in order to get previous states + */ +uint8_t lfsr_rollback_byte(struct Crypto1State *s, uint32_t in, int fb) +{ + /* + int i, ret = 0; + for (i = 7; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BIT(in, i), fb) << i; +*/ +// unfold loop 20160112 + uint8_t ret = 0; + ret |= lfsr_rollback_bit(s, BIT(in, 7), fb) << 7; + ret |= lfsr_rollback_bit(s, BIT(in, 6), fb) << 6; + ret |= lfsr_rollback_bit(s, BIT(in, 5), fb) << 5; + ret |= lfsr_rollback_bit(s, BIT(in, 4), fb) << 4; + ret |= lfsr_rollback_bit(s, BIT(in, 3), fb) << 3; + ret |= lfsr_rollback_bit(s, BIT(in, 2), fb) << 2; + ret |= lfsr_rollback_bit(s, BIT(in, 1), fb) << 1; + ret |= lfsr_rollback_bit(s, BIT(in, 0), fb) << 0; + return ret; +} +/** lfsr_rollback_word + * Rollback the shift register in order to get previous states + */ +uint32_t lfsr_rollback_word(struct Crypto1State *s, uint32_t in, int fb) +{ + /* + int i; + uint32_t ret = 0; + for (i = 31; i >= 0; --i) + ret |= lfsr_rollback_bit(s, BEBIT(in, i), fb) << (i ^ 24); +*/ +// unfold loop 20160112 + uint32_t ret = 0; + ret |= lfsr_rollback_bit(s, BEBIT(in, 31), fb) << (31 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 30), fb) << (30 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 29), fb) << (29 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 28), fb) << (28 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 27), fb) << (27 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 26), fb) << (26 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 25), fb) << (25 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 24), fb) << (24 ^ 24); + + ret |= lfsr_rollback_bit(s, BEBIT(in, 23), fb) << (23 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 22), fb) << (22 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 21), fb) << (21 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 20), fb) << (20 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 19), fb) << (19 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 18), fb) << (18 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 17), fb) << (17 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 16), fb) << (16 ^ 24); + + ret |= lfsr_rollback_bit(s, BEBIT(in, 15), fb) << (15 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 14), fb) << (14 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 13), fb) << (13 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 12), fb) << (12 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 11), fb) << (11 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 10), fb) << (10 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 9), fb) << (9 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 8), fb) << (8 ^ 24); + + ret |= lfsr_rollback_bit(s, BEBIT(in, 7), fb) << (7 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 6), fb) << (6 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 5), fb) << (5 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 4), fb) << (4 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 3), fb) << (3 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 2), fb) << (2 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 1), fb) << (1 ^ 24); + ret |= lfsr_rollback_bit(s, BEBIT(in, 0), fb) << (0 ^ 24); + return ret; +} + +/** nonce_distance + * x,y valid tag nonces, then prng_successor(x, nonce_distance(x, y)) = y + */ +static uint16_t *dist = 0; +int nonce_distance(uint32_t from, uint32_t to) +{ + uint16_t x, i; + if(!dist) { + dist = malloc(2 << 16); + if(!dist) + return -1; + for (x = i = 1; i; ++i) { + dist[(x & 0xff) << 8 | x >> 8] = i; + x = x >> 1 | (x ^ x >> 2 ^ x >> 3 ^ x >> 5) << 15; + } + } + return (65535 + dist[to >> 16] - dist[from >> 16]) % 65535; +} + + +static uint32_t fastfwd[2][8] = { + { 0, 0x4BC53, 0xECB1, 0x450E2, 0x25E29, 0x6E27A, 0x2B298, 0x60ECB}, + { 0, 0x1D962, 0x4BC53, 0x56531, 0xECB1, 0x135D3, 0x450E2, 0x58980}}; + + +/** lfsr_prefix_ks + * + * Is an exported helper function from the common prefix attack + * Described in the "dark side" paper. It returns an -1 terminated array + * of possible partial(21 bit) secret state. + * The required keystream(ks) needs to contain the keystream that was used to + * encrypt the NACK which is observed when varying only the 3 last bits of Nr + * only correct iff [NR_3] ^ NR_3 does not depend on Nr_3 + */ +uint32_t *lfsr_prefix_ks(uint8_t ks[8], int isodd) +{ + uint32_t *candidates = malloc(4 << 10); + if(!candidates) return 0; + + uint32_t c, entry; + int size = 0, i, good; + + for(i = 0; i < 1 << 21; ++i) { + for(c = 0, good = 1; good && c < 8; ++c) { + entry = i ^ fastfwd[isodd][c]; + good &= (BIT(ks[c], isodd) == filter(entry >> 1)); + good &= (BIT(ks[c], isodd + 2) == filter(entry)); + } + if(good) + candidates[size++] = i; + } + + candidates[size] = -1; + + return candidates; +} + +/** check_pfx_parity + * helper function which eliminates possible secret states using parity bits + */ +static struct Crypto1State* check_pfx_parity(uint32_t prefix, uint32_t rresp, uint8_t parities[8][8], uint32_t odd, uint32_t even, struct Crypto1State* sl) +{ + uint32_t ks1, nr, ks2, rr, ks3, c, good = 1; + + for(c = 0; good && c < 8; ++c) { + sl->odd = odd ^ fastfwd[1][c]; + sl->even = even ^ fastfwd[0][c]; + + lfsr_rollback_bit(sl, 0, 0); + lfsr_rollback_bit(sl, 0, 0); + + ks3 = lfsr_rollback_bit(sl, 0, 0); + ks2 = lfsr_rollback_word(sl, 0, 0); + ks1 = lfsr_rollback_word(sl, prefix | c << 5, 1); + + nr = ks1 ^ (prefix | c << 5); + rr = ks2 ^ rresp; + + good &= parity(nr & 0x000000ff) ^ parities[c][3] ^ BIT(ks2, 24); + good &= parity(rr & 0xff000000) ^ parities[c][4] ^ BIT(ks2, 16); + good &= parity(rr & 0x00ff0000) ^ parities[c][5] ^ BIT(ks2, 8); + good &= parity(rr & 0x0000ff00) ^ parities[c][6] ^ BIT(ks2, 0); + good &= parity(rr & 0x000000ff) ^ parities[c][7] ^ ks3; + } + + return sl + good; +} + +/** lfsr_common_prefix + * Implentation of the common prefix attack. + * Requires the 28 bit constant prefix used as reader nonce (pfx) + * The reader response used (rr) + * The keystream used to encrypt the observed NACK's (ks) + * The parity bits (par) + * It returns a zero terminated list of possible cipher states after the + * tag nonce was fed in + */ + +struct Crypto1State* lfsr_common_prefix(uint32_t pfx, uint32_t rr, uint8_t ks[8], uint8_t par[8][8]) +{ + struct Crypto1State *statelist, *s; + uint32_t *odd, *even, *o, *e, top; + + odd = lfsr_prefix_ks(ks, 1); + even = lfsr_prefix_ks(ks, 0); + + s = statelist = malloc((sizeof *statelist) << 20); + if(!s || !odd || !even) { + free(statelist); + statelist = 0; + goto out; + } + + for(o = odd; *o + 1; ++o) + for(e = even; *e + 1; ++e) + for(top = 0; top < 64; ++top) { + *o += 1 << 21; + *e += (!(top & 7) + 1) << 21; + s = check_pfx_parity(pfx, rr, par, *o, *e, s); + } + + s->odd = s->even = 0; +out: + free(odd); + free(even); + return statelist; +}