ADD: 'hf mf fastchk' - new command, improved check keys functionality. It uses a bunch of techniques to get a speedup.

Using a dictionary file with 421keys,

Current implementation of checkkeys takes 300 sec.
This implementation of checkkeys takes 250 sec.

I implemented it as a separate command so it will be easier to compare between the old and new checkkeys.
Its also doing much on deviceside, which is a step to much funnier standalone modes  :))
This commit is contained in:
iceman1001
2017-10-05 16:00:56 +02:00
parent b4a03581c2
commit 2ca0ea8cb4
6 changed files with 693 additions and 12 deletions

View File

@@ -26,9 +26,6 @@
#include "LCD.h"
#endif
// Global var to determine if device is in standalone mode or not.
static int InStandAloneMode = 0;
//=============================================================================
// A buffer where we can queue things up to be sent through the FPGA, for
// any purpose (fake tag, as reader, whatever). We go MSB first, since that
@@ -364,7 +361,6 @@ void SendStatus(void) {
// Show some leds in a pattern to identify StandAlone mod is running
void StandAloneMode(void) {
InStandAloneMode = 1;
DbpString("Stand-alone mode! No PC necessary.");
// Oooh pretty -- notify user we're in elite samy mode now
LED(LED_RED, 200);
@@ -403,7 +399,9 @@ void printStandAloneModes(void) {
#endif
DbpString("Running ");
Dbprintf(" Are we running standalone | %s", (InStandAloneMode)? "Yes" : "No");
//Dbprintf(" Is Device attached to USB| %s", USB_ATTACHED() ? "Yes" : "No");
//Dbprintf(" Is USB_reconnect value | %d", GetUSBreconnect() );
//Dbprintf(" Is USB_configured value | %d", GetUSBconfigured() );
//.. add your own standalone detection based on with compiler directive you are used.
// don't "reuse" the already taken ones, this will make things easier when trying to detect the different modes
@@ -818,9 +816,14 @@ void UsbPacketReceived(uint8_t *packet, int len) {
case CMD_MIFARE_NESTED:
MifareNested(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
break;
case CMD_MIFARE_CHKKEYS:
case CMD_MIFARE_CHKKEYS: {
MifareChkKeys(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
break;
}
case CMD_MIFARE_CHKKEYS_FAST: {
MifareChkKeys_fast(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
break;
}
case CMD_SIMULATE_MIFARE_CARD:
Mifare1ksim(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes);
break;
@@ -1127,8 +1130,6 @@ void __attribute__((noreturn)) AppMain(void) {
common_area.flags.osimage_present = 1;
LEDsoff();
// list with standalone refs.
// Init USB device
usb_enable();
@@ -1158,7 +1159,7 @@ void __attribute__((noreturn)) AppMain(void) {
byte_t rx[sizeof(UsbCommand)];
size_t rx_len;
for(;;) {
if ( usb_poll_validate_length() ) {
rx_len = usb_read(rx, sizeof(UsbCommand));
@@ -1184,7 +1185,9 @@ void __attribute__((noreturn)) AppMain(void) {
RunMod();
#endif
// when here, we are no longer in standalone mode.
InStandAloneMode = 0;
// reseting the variables which keeps track of usb re-attached/configured
//SetUSBreconnect(0);
//SetUSBconfigured(0);
}
}
}

View File

@@ -144,6 +144,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
void MifareAcquireNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint8_t *datain);
void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
void MifareChkKeys_fast(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
void Mifare1ksim(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain);
void MifareSetDbgLvl(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);
void MifareEMemClr(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datain);

View File

@@ -1957,7 +1957,61 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_card, uint32_
//set default timeout based on ATS
iso14a_set_ATS_timeout(resp);
}
return 1;
}
int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades) {
uint8_t wupa[] = { ISO14443A_CMD_WUPA }; // 0x26 - ISO14443A_CMD_REQA 0x52 - ISO14443A_CMD_WUPA
uint8_t sel_all[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x20 };
uint8_t sel_uid[] = { ISO14443A_CMD_ANTICOLL_OR_SELECT,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00};
uint8_t resp[5] = {0}; // theoretically. A usual RATS will be much smaller
uint8_t resp_par[1] = {0};
uint8_t uid_resp[4] = {0};
uint8_t sak = 0x04; // cascade uid
int cascade_level = 0;
// Broadcast for a card, WUPA (0x52) will force response from all cards in the field
ReaderTransmitBitsPar(wupa, 7, NULL, NULL);
// Receive the ATQA
if(!ReaderReceive(resp, resp_par)) return 0;
// OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in
// which case we need to make a cascade 2 request and select - this is a long UID
// While the UID is not complete, the 3nd bit (from the right) is set in the SAK.
for(; sak & 0x04; cascade_level++) {
// SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97)
sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2;
if (cascade_level < num_cascades - 1) {
uid_resp[0] = 0x88;
memcpy(uid_resp+1, uid_ptr+cascade_level*3, 3);
} else {
memcpy(uid_resp, uid_ptr+cascade_level*3, 4);
}
// Construct SELECT UID command
//sel_uid[1] = 0x70; // transmitting a full UID (1 Byte cmd, 1 Byte NVB, 4 Byte UID, 1 Byte BCC, 2 Bytes CRC)
memcpy(sel_uid+2, uid_resp, 4); // the UID received during anticollision, or the provided UID
sel_uid[6] = sel_uid[2] ^ sel_uid[3] ^ sel_uid[4] ^ sel_uid[5]; // calculate and add BCC
AppendCrc14443a(sel_uid, 7); // calculate and add CRC
ReaderTransmit(sel_uid, sizeof(sel_uid), NULL);
// Receive the SAK
if (!ReaderReceive(resp, resp_par)) return 0;
sak = resp[0];
// Test if more parts of the uid are coming
if ((sak & 0x04) /* && uid_resp[0] == 0x88 */) {
// Remove first byte, 0x88 is not an UID byte, it CT, see page 3 of:
// http://www.nxp.com/documents/application_note/AN10927.pdf
uid_resp[0] = uid_resp[1];
uid_resp[1] = uid_resp[2];
uid_resp[2] = uid_resp[3];
}
}
return 1;
}

View File

@@ -94,6 +94,7 @@ extern int ReaderReceive(uint8_t *receivedAnswer, uint8_t *par);
extern void iso14443a_setup(uint8_t fpga_minor_mode);
extern int iso14_apdu(uint8_t *cmd, uint16_t cmd_len, void *data);
extern int iso14443a_select_card(uint8_t *uid_ptr, iso14a_card_select_t *resp_data, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades, bool no_rats);
extern int iso14443a_fast_select_card(uint8_t *uid_ptr, uint8_t num_cascades);
extern void iso14a_set_trigger(bool enable);
int EmSendCmd14443aRaw(uint8_t *resp, uint16_t respLen);

View File

@@ -773,7 +773,8 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
}
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 (!iso14443a_fast_select_card(uid, cascade_levels)) {
//if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels, true)) {
if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)");
continue;
}
@@ -1053,6 +1054,315 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat
// 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;
// wait for the card to become ready again
// assume: fast reset of card
/*void chk_timeout(void){
uint8_t dummy_answer = 0;
ReaderTransmit(&dummy_answer, 1, NULL);
uint32_t timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT;
while(GetCountSspClk() < timeout);
}
*/
#ifndef CHK_TIMEOUT
# define CHK_TIMEOUT() {\
uint8_t dummy_answer = 0; \
ReaderTransmit(&dummy_answer, 1, NULL); \
uint32_t timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; \
while(GetCountSspClk() < timeout); \
}
#endif
// checks one key.
// fast select, tries 10times 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<10 ) {
// 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();
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);
CHK_TIMEOUT();
// 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;
}
}
return res;
}
void chkKey_scanA(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
// keep track of how many sectors on card.
for (uint8_t s = 0; s < *sectorcnt; ++s) {
// skip already found A keys
if( !found[(s*2)] ) {
c->block = FirstBlockOfSector( s );
uint8_t status = chkKey( c );
if ( status == 0 ) {
num_to_bytes(c->key, 6, k_sector[s].keyA);
found[(s*2)] = 1;
++*foundkeys;
}
}
}
}
void chkKey_scanB(struct chk_t *c, struct sector_t *k_sector, uint8_t *found, uint8_t *sectorcnt, uint8_t *foundkeys) {
// keep track of how many sectors on card.
for (uint8_t s = 0; s < *sectorcnt; ++s) {
// skip already found B keys
if( !found[(s*2)+1] ) {
c->block = FirstBlockOfSector( s );
uint8_t status = chkKey( c );
if ( status == 0 ) {
num_to_bytes(c->key, 6, k_sector[s].keyB);
found[(s*2)+1] = 1;
++*foundkeys;
}
}
}
}
// 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) {
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;
// 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->block = 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) {
// save old debuglevel, and tempory turn off dbg printing. speedissues.
int OLD_MF_DBGLEVEL = MF_DBGLEVEL;
MF_DBGLEVEL = MF_DBG_NONE;
// first call or
uint8_t sectorcnt = arg0 & 0xFF; // 16;
uint8_t firstchunk = (arg0 >> 8) & 0xF;
uint8_t lastchunk = (arg0 >> 12) & 0xF;
uint8_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;
if (uid == NULL || firstchunk) {
uid = BigBuf_malloc(10);
if (uid == NULL ) {
Dbprintf("ChkKeys: uid malloc failed");
goto OUT;
}
}
LEDsoff();
LED_A_ON();
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
if ( firstchunk ) {
clear_trace();
set_tracing(false);
memset(k_sector, 0x00, 480+10);
memset(found, 0x00, 80);
foundkeys = 0;
iso14a_card_select_t card_info;
if(!iso14443a_select_card(uid, &card_info, &cuid, true, 0, true)) {
Dbprintf("ChkKeys: 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;
// Keychunk loop
for (uint8_t i = 0; i < keyCount; ++i) {
// Allow button press / usb cmd to interrupt device
if (BUTTON_PRESS() && !usb_poll_validate_length()) break;
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) {
// 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, &sectorcnt, &foundkeys);
}
}
// 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, &sectorcnt, &foundkeys);
}
}
} // end loop sectors
// read Block B, if A is found.
chkKey_loopBonly( &chk_data, k_sector, found, &sectorcnt, &foundkeys);
// is all keys found?
if ( foundkeys == allkeys )
break;
} // end loop keys
OUT:
// restore debug level
MF_DBGLEVEL = OLD_MF_DBGLEVEL;
LEDsoff();
// All keys found, send to client, or last keychunk from client
if (foundkeys==allkeys || lastchunk ) {
uint64_t foo = 0;
uint16_t bar = 0;
for (uint8_t m = 0; m < 64; ++m)
foo |= (found[m] << m);
for (uint8_t m=64; m < sizeof(found); ++m)
bar |= (found[m] << (m-64));
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);
crypto1_destroy(pcs);
} 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) {
uint8_t blockNo = arg0 & 0xFF;