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
This commit is contained in:
pwpiwi 2015-10-25 19:34:33 +01:00
commit af57d9d5c6
3 changed files with 384 additions and 168 deletions

View file

@ -618,7 +618,6 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
pcs = &mpcs; pcs = &mpcs;
uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE]; uint8_t receivedAnswer[MAX_MIFARE_FRAME_SIZE];
int16_t isOK = 0; int16_t isOK = 0;
uint8_t nt_enc1[4];
uint8_t par_enc[1]; uint8_t par_enc[1];
uint8_t nt_par_enc = 0; uint8_t nt_par_enc = 0;
uint8_t buf[USB_CMD_DATA_SIZE]; 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); ui64Key = bytes_to_num(datain, 6);
bool initialize = flags & 0x0001; bool initialize = flags & 0x0001;
bool slow = flags & 0x0002; bool slow = flags & 0x0002;
bool field_off = flags & 0x0004;
#define AUTHENTICATION_TIMEOUT 848 // card times out 1ms after wrong authentication according to NXP documentation
#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 #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_A_ON();
LED_C_OFF(); LED_C_OFF();
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
if (initialize) { if (initialize) {
iso14443a_setup(FPGA_HF_ISO14443A_READER_LISTEN);
clear_trace(); clear_trace();
set_tracing(true); set_tracing(true);
} }
@ -648,11 +648,12 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
uint16_t num_nonces = 0; uint16_t num_nonces = 0;
bool have_uid = false; 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 // Test if the action was cancelled
if(BUTTON_PRESS()) { if(BUTTON_PRESS()) {
isOK = -2; isOK = 2;
field_off = true;
break; break;
} }
@ -670,7 +671,7 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
} }
have_uid = true; have_uid = true;
} else { // no need for anticollision. We can directly select the card } 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)"); if (MF_DBGLEVEL >= 1) Dbprintf("AcquireNonces: Can't select card (UID)");
continue; continue;
} }
@ -701,22 +702,18 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
num_nonces++; num_nonces++;
if (num_nonces % 2) { if (num_nonces % 2) {
memcpy(nt_enc1, receivedAnswer, 4); memcpy(buf+i, receivedAnswer, 4);
nt_par_enc = par_enc[0]; nt_par_enc = par_enc[0] & 0xf0;
} else { } else {
nt_par_enc |= par_enc[0]; nt_par_enc |= par_enc[0] >> 4;
memcpy(&buf[i], nt_enc1, 4); memcpy(buf+i+4, receivedAnswer, 4);
i += 4; memcpy(buf+i+8, &nt_par_enc, 1);
memcpy(&buf[i], receivedAnswer, 4); i += 9;
i += 4;
memcpy(&buf[i], &nt_par_enc, 1);
i += 1;
} }
// wait for the card to become ready again // wait for the card to become ready again
while(GetCountSspClk() < timeout); while(GetCountSspClk() < timeout);
} }
LED_C_OFF(); LED_C_OFF();
@ -724,14 +721,15 @@ void MifareAcquireEncryptedNonces(uint32_t arg0, uint32_t arg1, uint32_t flags,
crypto1_destroy(pcs); crypto1_destroy(pcs);
LED_B_ON(); LED_B_ON();
memcpy(&cuid, uid+(cascade_levels-1)*3, 4);
cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf)); cmd_send(CMD_ACK, isOK, cuid, num_nonces, buf, sizeof(buf));
LED_B_OFF(); LED_B_OFF();
if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished"); if (MF_DBGLEVEL >= 3) DbpString("AcquireEncryptedNonces finished");
FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF); if (field_off) {
LEDsoff(); FpgaWriteConfWord(FPGA_MAJOR_MODE_OFF);
LEDsoff();
}
} }

View file

@ -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); int16_t isOK = mfnestedhard(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_read, nonce_file_write, slow);
if (isOK) { if (isOK) {
switch (isOK) { switch (isOK) {
case -1 : PrintAndLog("Error: No response from Proxmark.\n"); break; case 1 : PrintAndLog("Error: No response from Proxmark.\n"); break;
case -2 : PrintAndLog("Button pressed. Aborted.\n"); break; case 2 : PrintAndLog("Button pressed. Aborted.\n"); break;
case 3 : PrintAndLog("File error. Aborted.\n"); break;
default : PrintAndLog("Unknown Error.\n"); default : PrintAndLog("Unknown Error.\n");
} }
return 2; return 2;

View file

@ -12,6 +12,7 @@
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <pthread.h> #include <pthread.h>
#include <math.h>
#include "mifarehost.h" #include "mifarehost.h"
#include "proxmark3.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 { typedef struct noncelistentry {
uint32_t NonceAndPar; // concatenated last 24 bits of nonce and parity uint32_t nonce_enc;
uint8_t par_enc;
void *next; void *next;
} noncelistentry_t; } noncelistentry_t;
typedef struct noncelist { typedef struct noncelist {
uint16_t num; uint16_t num;
uint8_t Sum; uint16_t Sum;
bool updated;
noncelistentry_t *first; noncelistentry_t *first;
} noncelist_t; } noncelist_t;
noncelist_t nonces[256]; static noncelist_t nonces[256];
uint16_t first_byte_Sum = 0; static uint16_t first_byte_Sum = 0;
uint16_t first_byte_num = 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; uint8_t first_byte = nonce_enc >> 24;
uint32_t NonceAndPar = (nonce << 8) | (par & 0x07);
noncelistentry_t *p1 = nonces[first_byte].first; noncelistentry_t *p1 = nonces[first_byte].first;
noncelistentry_t *p2 = NULL; 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_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; p2 = p1;
p1 = p1->next; p1 = p1->next;
} }
if (p1 == NULL) { // need to add at the end of the list if (p1 == NULL) { // need to add at the end of the list
if (p2 == NULL) { // list is empty yet 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)); p2 = nonces[first_byte].first = malloc(sizeof(noncelistentry_t));
} else { } else {
p2 = p2->next = malloc(sizeof(noncelistentry_t)); 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 } else { // we have seen this 2nd byte before. Nothing to add or insert.
if (p2 == NULL) { // need to insert at start of list return (0);
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);
} }
// add or insert new data
p2->next = p1; p2->next = p1;
p2->NonceAndPar = NonceAndPar; p2->nonce_enc = nonce_enc;
nonces[first_byte].num++; p2->par_enc = par_enc;
nonces[first_byte].Sum += parity(NonceAndPar & 0xff000004); // 2nd byte sum property
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; uint16_t oddsum = 0;
for (uint16_t j = 0; j < 16; j++) { 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; uint16_t evensum = 0;
for (uint16_t j = 0; j < 16; j++) { 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_odd = SumPropertyOdd(s);
uint16_t sum_even = SumPropertyEven(s); uint16_t sum_even = SumPropertyEven(s);
return (sum_odd*(16-sum_even) + (16-sum_odd)*sum_even); 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]; uint32_t statistics[257];
struct Crypto1State cs; struct Crypto1State cs;
time_t time1 = clock();
for (uint16_t i = 0; i < 257; i++) { for (uint16_t i = 0; i < 257; i++) {
statistics[i] = 0; statistics[i] = 0;
} }
time_t time1 = clock();
#define NUM_STATISTICS 1000000 for (uint64_t i = 0; i < NUM_STATISTICS; i++) {
for (uint32_t i = 0; i < NUM_STATISTICS; i++) {
cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff); cs.odd = (rand() & 0xfff) << 12 | (rand() & 0xfff);
cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff); cs.even = (rand() & 0xfff) << 12 | (rand() & 0xfff);
uint16_t sum_property = SumProperty(&cs); uint16_t sum_property = SumProperty(&cs);
@ -313,148 +419,259 @@ void TestSumProperty()
if (i%(NUM_STATISTICS/100) == 0) printf("."); 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++) { for (uint16_t i = 0; i < 257; i++) {
if (statistics[i] != 0) { if (statistics[i] != 0) {
printf("probability[%3d] = %0.5f\n", i, (float)statistics[i]/NUM_STATISTICS); 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)
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) {
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; FILE *fnonces = NULL;
uint32_t total_num_nonces = 0; uint32_t cuid;
uint32_t flags = 0; uint8_t trgBlockNo;
bool initialize = true; uint8_t trgKeyType;
clock_t time1;
uint8_t read_buf[9]; uint8_t read_buf[9];
uint32_t nt_enc1, nt_enc2; uint32_t nt_enc1, nt_enc2;
uint8_t par_enc; 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; 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 // initialize the list of nonces
for (uint16_t i = 0; i < 256; i++) { for (uint16_t i = 0; i < 256; i++) {
nonces[i].num = 0; nonces[i].num = 0;
nonces[i].Sum = 0; nonces[i].Sum = 0;
nonces[i].first = NULL; nonces[i].first = NULL;
nonces[i].updated = true;
} }
first_byte_num = 0; first_byte_num = 0;
first_byte_Sum = 0; first_byte_Sum = 0;
guessed_Sum8 = 0;
best_first_byte = 0;
guessed_Sum8_confidence = 0.0;
//StateList_t statelists[2]; //StateList_t statelists[2];
//struct Crypto1State *p1, *p2, *p3, *p4; //struct Crypto1State *p1, *p2, *p3, *p4;
if (nonce_file_read) { if (nonce_file_read) { // use pre-acquired data from file nonces.bin
// don't acquire nonces, use pre-acquired data from file nonces.bin if (read_nonce_file() != 0) {
if ((fnonces = fopen("nonces.bin","rb")) == NULL) { return 3;
PrintAndLog("Could not open file nonces.bin");
return 1;
} }
PrintAndLog("Reading nonces from file nonces.bin..."); guessed_Sum8_confidence = estimate_second_byte_sum();
if (fread(read_buf, 1, 6, fnonces) == 0) { } else { // acquire nonces.
PrintAndLog("File reading error."); uint16_t is_OK = acquire_nonces(blockNo, keyType, key, trgBlockNo, trgKeyType, nonce_file_write, slow);
fclose(fnonces); if (is_OK != 0) {
return 1; 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)) { Tests();
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. PrintAndLog("");
time1 = clock(); PrintAndLog("Sum(a0) = %d", first_byte_Sum);
do { PrintAndLog("Guess for Sum(a8) = %d for first nonce byte = 0x%02x, n = %d, k = %d, probability for correct guess = %1.0f%%\n",
clearCommandBuffer(); guessed_Sum8,
flags |= initialize ? 0x0001 : 0; best_first_byte,
flags |= slow ? 0x0002 : 0; nonces[best_first_byte].num,
UsbCommand c = {CMD_MIFARE_ACQUIRE_ENCRYPTED_NONCES, {blockNo + keyType * 0x100, trgBlockNo + trgKeyType * 0x100, flags}}; nonces[best_first_byte].Sum,
memcpy(c.d.asBytes, key, 6); guessed_Sum8_confidence*100);
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("Generation of candidate list and brute force phase not yet implemented"); PrintAndLog("Generation of candidate list and brute force phase not yet implemented");