mirror of
https://github.com/Proxmark/proxmark3.git
synced 2025-07-07 13:41:18 -07:00
hf mf mifare: (finally) fix watchdog reset
- minor changes to sync - try alternative strategies when debugging
This commit is contained in:
parent
dfb387bf0f
commit
8c6b22980c
3 changed files with 68 additions and 18 deletions
|
@ -2018,9 +2018,10 @@ void ReaderMifare(bool first_try)
|
||||||
|
|
||||||
#define PRNG_SEQUENCE_LENGTH (1 << 16);
|
#define PRNG_SEQUENCE_LENGTH (1 << 16);
|
||||||
static uint32_t sync_time;
|
static uint32_t sync_time;
|
||||||
static uint32_t sync_cycles;
|
static int32_t sync_cycles;
|
||||||
int catch_up_cycles = 0;
|
int catch_up_cycles = 0;
|
||||||
int last_catch_up = 0;
|
int last_catch_up = 0;
|
||||||
|
uint16_t elapsed_prng_sequences;
|
||||||
uint16_t consecutive_resyncs = 0;
|
uint16_t consecutive_resyncs = 0;
|
||||||
int isOK = 0;
|
int isOK = 0;
|
||||||
|
|
||||||
|
@ -2029,7 +2030,6 @@ void ReaderMifare(bool first_try)
|
||||||
sync_time = GetCountSspClk() & 0xfffffff8;
|
sync_time = GetCountSspClk() & 0xfffffff8;
|
||||||
sync_cycles = PRNG_SEQUENCE_LENGTH; // theory: Mifare Classic's random generator repeats every 2^16 cycles (and so do the tag nonces).
|
sync_cycles = PRNG_SEQUENCE_LENGTH; // theory: Mifare Classic's random generator repeats every 2^16 cycles (and so do the tag nonces).
|
||||||
nt_attacked = 0;
|
nt_attacked = 0;
|
||||||
nt = 0;
|
|
||||||
par[0] = 0;
|
par[0] = 0;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -2045,11 +2045,16 @@ void ReaderMifare(bool first_try)
|
||||||
|
|
||||||
|
|
||||||
#define MAX_UNEXPECTED_RANDOM 4 // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up.
|
#define MAX_UNEXPECTED_RANDOM 4 // maximum number of unexpected (i.e. real) random numbers when trying to sync. Then give up.
|
||||||
#define MAX_SYNC_TRIES 16
|
#define MAX_SYNC_TRIES 32
|
||||||
|
#define NUM_DEBUG_INFOS 8 // per strategy
|
||||||
|
#define MAX_STRATEGY 3
|
||||||
uint16_t unexpected_random = 0;
|
uint16_t unexpected_random = 0;
|
||||||
uint16_t sync_tries = 0;
|
uint16_t sync_tries = 0;
|
||||||
int16_t debug_info_nr = -1;
|
int16_t debug_info_nr = -1;
|
||||||
uint32_t debug_info[MAX_SYNC_TRIES];
|
uint16_t strategy = 0;
|
||||||
|
int32_t debug_info[MAX_STRATEGY][NUM_DEBUG_INFOS];
|
||||||
|
uint32_t select_time;
|
||||||
|
uint32_t halt_time;
|
||||||
|
|
||||||
for(uint16_t i = 0; TRUE; i++) {
|
for(uint16_t i = 0; TRUE; i++) {
|
||||||
|
|
||||||
|
@ -2062,24 +2067,59 @@ void ReaderMifare(bool first_try)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (strategy == 2) {
|
||||||
|
// test with additional hlt command
|
||||||
|
halt_time = 0;
|
||||||
|
int len = mifare_sendcmd_short(NULL, false, 0x50, 0x00, receivedAnswer, receivedAnswerPar, &halt_time);
|
||||||
|
if (len && MF_DBGLEVEL >= 3) {
|
||||||
|
Dbprintf("Unexpected response of %d bytes to hlt command (additional debugging).", len);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strategy == 3) {
|
||||||
|
// test with FPGA power off/on
|
||||||
|
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
|
||||||
|
SpinDelay(200);
|
||||||
|
iso14443a_setup(FPGA_HF_ISO14443A_READER_MOD);
|
||||||
|
SpinDelay(100);
|
||||||
|
}
|
||||||
|
|
||||||
if(!iso14443a_select_card(uid, NULL, &cuid)) {
|
if(!iso14443a_select_card(uid, NULL, &cuid)) {
|
||||||
if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card");
|
if (MF_DBGLEVEL >= 1) Dbprintf("Mifare: Can't select card");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
select_time = GetCountSspClk();
|
||||||
|
|
||||||
|
elapsed_prng_sequences = 1;
|
||||||
if (debug_info_nr == -1) {
|
if (debug_info_nr == -1) {
|
||||||
sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles;
|
sync_time = (sync_time & 0xfffffff8) + sync_cycles + catch_up_cycles;
|
||||||
catch_up_cycles = 0;
|
catch_up_cycles = 0;
|
||||||
|
|
||||||
// if we missed the sync time already, advance to the next nonce repeat
|
// if we missed the sync time already, advance to the next nonce repeat
|
||||||
while(GetCountSspClk() > sync_time) {
|
while(GetCountSspClk() > sync_time) {
|
||||||
|
elapsed_prng_sequences++;
|
||||||
sync_time = (sync_time & 0xfffffff8) + sync_cycles;
|
sync_time = (sync_time & 0xfffffff8) + sync_cycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Transmit MIFARE_CLASSIC_AUTH at synctime. Should result in returning the same tag nonce (== nt_attacked)
|
// Transmit MIFARE_CLASSIC_AUTH at synctime. Should result in returning the same tag nonce (== nt_attacked)
|
||||||
ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time);
|
ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time);
|
||||||
} else {
|
} else {
|
||||||
ReaderTransmit(mf_auth, sizeof(mf_auth), NULL);
|
// collect some information on tag nonces for debugging:
|
||||||
|
#define DEBUG_FIXED_SYNC_CYCLES PRNG_SEQUENCE_LENGTH
|
||||||
|
if (strategy == 0) {
|
||||||
|
// nonce distances at fixed time after card select:
|
||||||
|
sync_time = select_time + DEBUG_FIXED_SYNC_CYCLES;
|
||||||
|
} else if (strategy == 1) {
|
||||||
|
// nonce distances at fixed time between authentications:
|
||||||
|
sync_time = sync_time + DEBUG_FIXED_SYNC_CYCLES;
|
||||||
|
} else if (strategy == 2) {
|
||||||
|
// nonce distances at fixed time after halt:
|
||||||
|
sync_time = halt_time + DEBUG_FIXED_SYNC_CYCLES;
|
||||||
|
} else {
|
||||||
|
// nonce_distances at fixed time after power on
|
||||||
|
sync_time = DEBUG_FIXED_SYNC_CYCLES;
|
||||||
|
}
|
||||||
|
ReaderTransmit(mf_auth, sizeof(mf_auth), &sync_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Receive the (4 Byte) "random" nonce
|
// Receive the (4 Byte) "random" nonce
|
||||||
|
@ -2101,7 +2141,7 @@ void ReaderMifare(bool first_try)
|
||||||
} else {
|
} else {
|
||||||
if (nt_distance == -99999) { // invalid nonce received
|
if (nt_distance == -99999) { // invalid nonce received
|
||||||
unexpected_random++;
|
unexpected_random++;
|
||||||
if (!nt_attacked && unexpected_random > MAX_UNEXPECTED_RANDOM) {
|
if (unexpected_random > MAX_UNEXPECTED_RANDOM) {
|
||||||
isOK = -3; // Card has an unpredictable PRNG. Give up
|
isOK = -3; // Card has an unpredictable PRNG. Give up
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
|
@ -2109,20 +2149,25 @@ void ReaderMifare(bool first_try)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (++sync_tries > MAX_SYNC_TRIES) {
|
if (++sync_tries > MAX_SYNC_TRIES) {
|
||||||
if (sync_tries > 2 * MAX_SYNC_TRIES) {
|
if (strategy > MAX_STRATEGY || MF_DBGLEVEL < 3) {
|
||||||
isOK = -4; // Card's PRNG runs at an unexpected frequency or resets unexpectedly
|
isOK = -4; // Card's PRNG runs at an unexpected frequency or resets unexpectedly
|
||||||
break;
|
break;
|
||||||
} else { // continue for a while, just to collect some debug info
|
} else { // continue for a while, just to collect some debug info
|
||||||
debug_info[++debug_info_nr] = nt_distance;
|
debug_info[strategy][debug_info_nr] = nt_distance;
|
||||||
|
debug_info_nr++;
|
||||||
|
if (debug_info_nr == NUM_DEBUG_INFOS) {
|
||||||
|
strategy++;
|
||||||
|
debug_info_nr = 0;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sync_cycles = (sync_cycles - nt_distance);
|
sync_cycles = (sync_cycles - nt_distance/elapsed_prng_sequences);
|
||||||
if (sync_cycles <= 0) {
|
if (sync_cycles <= 0) {
|
||||||
sync_cycles += PRNG_SEQUENCE_LENGTH;
|
sync_cycles += PRNG_SEQUENCE_LENGTH;
|
||||||
}
|
}
|
||||||
if (MF_DBGLEVEL >= 3) {
|
if (MF_DBGLEVEL >= 3) {
|
||||||
Dbprintf("calibrating in cycle %d. nt_distance=%d, Sync_cycles: %d\n", i, nt_distance, sync_cycles);
|
Dbprintf("calibrating in cycle %d. nt_distance=%d, elapsed_prng_sequences=%d, new sync_cycles: %d\n", i, nt_distance, elapsed_prng_sequences, sync_cycles);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2134,6 +2179,7 @@ void ReaderMifare(bool first_try)
|
||||||
catch_up_cycles = 0;
|
catch_up_cycles = 0;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
catch_up_cycles /= elapsed_prng_sequences;
|
||||||
if (catch_up_cycles == last_catch_up) {
|
if (catch_up_cycles == last_catch_up) {
|
||||||
consecutive_resyncs++;
|
consecutive_resyncs++;
|
||||||
}
|
}
|
||||||
|
@ -2147,6 +2193,9 @@ void ReaderMifare(bool first_try)
|
||||||
else {
|
else {
|
||||||
sync_cycles = sync_cycles + catch_up_cycles;
|
sync_cycles = sync_cycles + catch_up_cycles;
|
||||||
if (MF_DBGLEVEL >= 3) Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, -catch_up_cycles, sync_cycles);
|
if (MF_DBGLEVEL >= 3) Dbprintf("Lost sync in cycle %d for the fourth time consecutively (nt_distance = %d). Adjusting sync_cycles to %d.\n", i, -catch_up_cycles, sync_cycles);
|
||||||
|
last_catch_up = 0;
|
||||||
|
catch_up_cycles = 0;
|
||||||
|
consecutive_resyncs = 0;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -2154,12 +2203,10 @@ void ReaderMifare(bool first_try)
|
||||||
consecutive_resyncs = 0;
|
consecutive_resyncs = 0;
|
||||||
|
|
||||||
// Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding
|
// Receive answer. This will be a 4 Bit NACK when the 8 parity bits are OK after decoding
|
||||||
if (ReaderReceive(receivedAnswer, receivedAnswerPar))
|
if (ReaderReceive(receivedAnswer, receivedAnswerPar)) {
|
||||||
{
|
|
||||||
catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer
|
catch_up_cycles = 8; // the PRNG is delayed by 8 cycles due to the NAC (4Bits = 0x05 encrypted) transfer
|
||||||
|
|
||||||
if (nt_diff == 0)
|
if (nt_diff == 0) {
|
||||||
{
|
|
||||||
par_low = par[0] & 0xE0; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change
|
par_low = par[0] & 0xE0; // there is no need to check all parities for other nt_diff. Parity Bits for mf_nr_ar[0..2] won't change
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2197,8 +2244,10 @@ void ReaderMifare(bool first_try)
|
||||||
|
|
||||||
if (isOK == -4) {
|
if (isOK == -4) {
|
||||||
if (MF_DBGLEVEL >= 3) {
|
if (MF_DBGLEVEL >= 3) {
|
||||||
for(uint16_t i = 0; i < MAX_SYNC_TRIES; i++) {
|
for (uint16_t i = 0; i <= MAX_STRATEGY; i++) {
|
||||||
Dbprintf("collected debug info[%d] = %d\n", i, debug_info[i]);
|
for(uint16_t j = 0; j < NUM_DEBUG_INFOS; j++) {
|
||||||
|
Dbprintf("collected debug info[%d][%d] = %d", i, j, debug_info[i][j]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,8 @@ start:
|
||||||
case -1 : PrintAndLog("Button pressed. Aborted.\n"); break;
|
case -1 : PrintAndLog("Button pressed. Aborted.\n"); break;
|
||||||
case -2 : PrintAndLog("Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests).\n"); break;
|
case -2 : PrintAndLog("Card is not vulnerable to Darkside attack (doesn't send NACK on authentication requests).\n"); break;
|
||||||
case -3 : PrintAndLog("Card is not vulnerable to Darkside attack (its random number generator is not predictable).\n"); break;
|
case -3 : PrintAndLog("Card is not vulnerable to Darkside attack (its random number generator is not predictable).\n"); break;
|
||||||
case -4 : PrintAndLog("The card's random number generator is vulnerable but behaves somewhat weird (Mifare clone?). This needs to be fixed.\n"); break;
|
case -4 : PrintAndLog("Card is not vulnerable to Darkside attack (its random number generator seems to be based on the wellknown");
|
||||||
|
PrintAndLog("generating polynomial with 16 effective bits only, but shows unexpected behaviour.\n"); break;
|
||||||
default: ;
|
default: ;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -113,7 +113,7 @@ function mfcrack_inner()
|
||||||
elseif isOK == 0xFFFFFFFD then
|
elseif isOK == 0xFFFFFFFD then
|
||||||
return nil, "Card is not vulnerable to Darkside attack (its random number generator is not predictable). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
|
return nil, "Card is not vulnerable to Darkside attack (its random number generator is not predictable). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
|
||||||
elseif isOK == 0xFFFFFFFC then
|
elseif isOK == 0xFFFFFFFC then
|
||||||
return nil, "The card's random number generator is vulnerable but behaves somewhat weird (Mifare clone?). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
|
return nil, "The card's random number generator behaves somewhat weird (Mifare clone?). You can try 'script run mfkeys' or 'hf mf chk' to test various known keys."
|
||||||
elseif isOK ~= 1 then
|
elseif isOK ~= 1 then
|
||||||
return nil, "Error occurred"
|
return nil, "Error occurred"
|
||||||
end
|
end
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue